|
|
@@ -139,30 +139,194 @@ impl BlockInfo {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// [`Block`] sled tree
|
|
|
+/// Auxiliary structure used to keep track of blocks order.
|
|
|
+#[derive(Debug, SerialEncodable, SerialDecodable)]
|
|
|
+pub struct BlockOrder {
|
|
|
+ /// Order number
|
|
|
+ pub number: u64,
|
|
|
+ /// Block headerhash of that number
|
|
|
+ pub block: blake3::Hash,
|
|
|
+}
|
|
|
+
|
|
|
+/// 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
|
|
|
+/// ranks, if ever needed.
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct BlockRanks {
|
|
|
+ /// Block target rank
|
|
|
+ pub target_rank: BigUint,
|
|
|
+ /// Height cummulative targets rank
|
|
|
+ pub targets_rank: BigUint,
|
|
|
+ /// Block hash rank
|
|
|
+ pub hash_rank: BigUint,
|
|
|
+ /// Height cummulative hashes rank
|
|
|
+ pub hashes_rank: BigUint,
|
|
|
+}
|
|
|
+
|
|
|
+impl BlockRanks {
|
|
|
+ pub fn new(
|
|
|
+ target_rank: BigUint,
|
|
|
+ targets_rank: BigUint,
|
|
|
+ hash_rank: BigUint,
|
|
|
+ hashes_rank: BigUint,
|
|
|
+ ) -> Self {
|
|
|
+ Self { target_rank, targets_rank, hash_rank, hashes_rank }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Note: Doing all the imports here as this might get obselete if
|
|
|
+// we implemented Encodable/Decodable for num_bigint::BigUint.
|
|
|
+impl darkfi_serial::Encodable for BlockRanks {
|
|
|
+ fn encode<S: std::io::Write>(&self, mut s: S) -> std::io::Result<usize> {
|
|
|
+ let mut len = 0;
|
|
|
+ len += self.target_rank.to_bytes_be().encode(&mut s)?;
|
|
|
+ len += self.targets_rank.to_bytes_be().encode(&mut s)?;
|
|
|
+ len += self.hash_rank.to_bytes_be().encode(&mut s)?;
|
|
|
+ len += self.hashes_rank.to_bytes_be().encode(&mut s)?;
|
|
|
+ Ok(len)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl darkfi_serial::Decodable for BlockRanks {
|
|
|
+ fn decode<D: std::io::Read>(mut d: D) -> std::io::Result<Self> {
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let target_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let targets_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let hash_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let hashes_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let ret = Self { target_rank, targets_rank, hash_rank, hashes_rank };
|
|
|
+ Ok(ret)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Auxiliary structure used to keep track of block PoW difficulty information.
|
|
|
+/// Note: we only need height cummulative difficulty, but we also keep its actual
|
|
|
+/// difficulty, so we can verify the sequence and/or know specific block height
|
|
|
+/// difficulty, if ever needed.
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct BlockDifficulty {
|
|
|
+ /// Block height number
|
|
|
+ pub height: u64,
|
|
|
+ /// Block creation timestamp
|
|
|
+ pub timestamp: Timestamp,
|
|
|
+ /// Height difficulty
|
|
|
+ pub difficulty: BigUint,
|
|
|
+ /// Height cummulative difficulty (total + height difficulty)
|
|
|
+ pub cummulative_difficulty: BigUint,
|
|
|
+ /// Block ranks
|
|
|
+ pub ranks: BlockRanks,
|
|
|
+}
|
|
|
+
|
|
|
+impl BlockDifficulty {
|
|
|
+ pub fn new(
|
|
|
+ height: u64,
|
|
|
+ timestamp: Timestamp,
|
|
|
+ difficulty: BigUint,
|
|
|
+ cummulative_difficulty: BigUint,
|
|
|
+ ranks: BlockRanks,
|
|
|
+ ) -> Self {
|
|
|
+ Self { height, timestamp, difficulty, cummulative_difficulty, ranks }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Represents the genesis block difficulty
|
|
|
+ pub fn genesis(timestamp: Timestamp) -> Self {
|
|
|
+ let ranks = BlockRanks::new(
|
|
|
+ BigUint::from(0u64),
|
|
|
+ BigUint::from(0u64),
|
|
|
+ BigUint::from(0u64),
|
|
|
+ BigUint::from(0u64),
|
|
|
+ );
|
|
|
+ BlockDifficulty::new(0, timestamp, BigUint::from(0u64), BigUint::from(0u64), ranks)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Note: Doing all the imports here as this might get obselete if
|
|
|
+// we implemented Encodable/Decodable for num_bigint::BigUint.
|
|
|
+impl darkfi_serial::Encodable for BlockDifficulty {
|
|
|
+ fn encode<S: std::io::Write>(&self, mut s: S) -> std::io::Result<usize> {
|
|
|
+ let mut len = 0;
|
|
|
+ len += self.height.encode(&mut s)?;
|
|
|
+ len += self.timestamp.encode(&mut s)?;
|
|
|
+ len += self.difficulty.to_bytes_be().encode(&mut s)?;
|
|
|
+ len += self.cummulative_difficulty.to_bytes_be().encode(&mut s)?;
|
|
|
+ len += self.ranks.encode(&mut s)?;
|
|
|
+ Ok(len)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl darkfi_serial::Decodable for BlockDifficulty {
|
|
|
+ fn decode<D: std::io::Read>(mut d: D) -> std::io::Result<Self> {
|
|
|
+ let height: u64 = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let timestamp: Timestamp = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let difficulty: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let cummulative_difficulty: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
+ let ranks: BlockRanks = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
+ let ret = Self { height, timestamp, difficulty, cummulative_difficulty, ranks };
|
|
|
+ Ok(ret)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
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 `BlockStore` is a `sled` tree storing all the blockchain's blocks
|
|
|
-/// where the key is the blocks' hash, and value is the serialized block.
|
|
|
+/// The `BlockStore` is a structure representing all `sled` trees related
|
|
|
+/// to storing the blockchain's blocks information.
|
|
|
#[derive(Clone)]
|
|
|
-pub struct BlockStore(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 BlockStore {
|
|
|
/// Opens a new or existing `BlockStore` on the given sled database.
|
|
|
pub fn new(db: &sled::Db) -> Result<Self> {
|
|
|
- let tree = db.open_tree(SLED_BLOCK_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 [`Block`] into the store.
|
|
|
+ /// Insert a slice of [`Block`] into the store's main tree.
|
|
|
pub fn insert(&self, blocks: &[Block]) -> Result<Vec<blake3::Hash>> {
|
|
|
let (batch, ret) = self.insert_batch(blocks)?;
|
|
|
- self.0.apply_batch(batch)?;
|
|
|
+ self.main.apply_batch(batch)?;
|
|
|
Ok(ret)
|
|
|
}
|
|
|
|
|
|
- /// Generate the sled batch corresponding to an insert, so caller
|
|
|
- /// can handle the write operation.
|
|
|
+ /// 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(())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 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.
|
|
|
@@ -179,86 +343,64 @@ impl BlockStore {
|
|
|
Ok((batch, ret))
|
|
|
}
|
|
|
|
|
|
- /// Check if the block store contains a given block hash.
|
|
|
- pub fn contains(&self, blockhash: &blake3::Hash) -> Result<bool> {
|
|
|
- Ok(self.0.contains_key(blockhash.as_bytes())?)
|
|
|
- }
|
|
|
+ /// 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<sled::Batch> {
|
|
|
+ if order.len() != hashes.len() {
|
|
|
+ return Err(Error::InvalidInputLengths)
|
|
|
+ }
|
|
|
|
|
|
- /// 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<Vec<Option<Block>>> {
|
|
|
- let mut ret = Vec::with_capacity(block_hashes.len());
|
|
|
+ let mut batch = sled::Batch::default();
|
|
|
|
|
|
- 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);
|
|
|
+ for (i, number) in order.iter().enumerate() {
|
|
|
+ batch.insert(&number.to_be_bytes(), hashes[i].as_bytes());
|
|
|
}
|
|
|
|
|
|
- Ok(ret)
|
|
|
+ Ok(batch)
|
|
|
}
|
|
|
|
|
|
- /// 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<Vec<(blake3::Hash, Block)>> {
|
|
|
- let mut blocks = vec![];
|
|
|
+ /// 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_difficulty(
|
|
|
+ &self,
|
|
|
+ block_difficulties: &[BlockDifficulty],
|
|
|
+ ) -> Result<sled::Batch> {
|
|
|
+ let mut batch = sled::Batch::default();
|
|
|
|
|
|
- for block in self.0.iter() {
|
|
|
- blocks.push(parse_record(block.unwrap())?);
|
|
|
+ for block_difficulty in block_difficulties {
|
|
|
+ batch.insert(&block_difficulty.height.to_be_bytes(), serialize(block_difficulty));
|
|
|
}
|
|
|
|
|
|
- Ok(blocks)
|
|
|
+ Ok(batch)
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/// Overlay structure over a [`BlockStore`] instance.
|
|
|
-pub struct BlockStoreOverlay(SledDbOverlayPtr);
|
|
|
|
|
|
-impl BlockStoreOverlay {
|
|
|
- pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
|
|
|
- overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE)?;
|
|
|
- Ok(Self(overlay.clone()))
|
|
|
+ /// Check if the store's main tree contains a given block hash.
|
|
|
+ pub fn contains(&self, blockhash: &blake3::Hash) -> Result<bool> {
|
|
|
+ Ok(self.main.contains_key(blockhash.as_bytes())?)
|
|
|
}
|
|
|
|
|
|
- /// 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<Vec<blake3::Hash>> {
|
|
|
- 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)
|
|
|
+ /// Check if the store's order tree contains a given order number.
|
|
|
+ pub fn contains_order(&self, number: u64) -> Result<bool> {
|
|
|
+ Ok(self.order.contains_key(number.to_be_bytes())?)
|
|
|
}
|
|
|
|
|
|
- /// Fetch given block hashes from the overlay.
|
|
|
+ /// 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 overlay, and otherwise it is `None`, if it has not.
|
|
|
+ /// 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<Vec<Option<Block>>> {
|
|
|
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())? {
|
|
|
+ if let Some(found) = self.main.get(hash.as_bytes())? {
|
|
|
let block = deserialize(&found)?;
|
|
|
ret.push(Some(block));
|
|
|
continue
|
|
|
@@ -272,72 +414,17 @@ impl BlockStoreOverlay {
|
|
|
|
|
|
Ok(ret)
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/// Auxiliary structure used to keep track of blocks order.
|
|
|
-#[derive(Debug, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct BlockOrder {
|
|
|
- /// Order number
|
|
|
- pub number: u64,
|
|
|
- /// Block headerhash of that number
|
|
|
- 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<Self> {
|
|
|
- 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<sled::Batch> {
|
|
|
- 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<bool> {
|
|
|
- Ok(self.0.contains_key(number.to_be_bytes())?)
|
|
|
- }
|
|
|
-
|
|
|
- /// Fetch given order numbers from the block order store.
|
|
|
+ /// 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(&self, order: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
|
|
|
+ pub fn get_order(&self, order: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
|
|
|
let mut ret = Vec::with_capacity(order.len());
|
|
|
|
|
|
for number in order {
|
|
|
- if let Some(found) = self.0.get(number.to_be_bytes())? {
|
|
|
+ if let Some(found) = self.order.get(number.to_be_bytes())? {
|
|
|
let block_hash = deserialize(&found)?;
|
|
|
ret.push(Some(block_hash));
|
|
|
continue
|
|
|
@@ -351,18 +438,72 @@ impl BlockOrderStore {
|
|
|
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<Vec<(u64, blake3::Hash)>> {
|
|
|
- let mut order = vec![];
|
|
|
-
|
|
|
- for record in self.0.iter() {
|
|
|
- order.push(parse_u64_key_record(record.unwrap())?);
|
|
|
- }
|
|
|
+ /// 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_difficulty(
|
|
|
+ &self,
|
|
|
+ heights: &[u64],
|
|
|
+ strict: bool,
|
|
|
+ ) -> Result<Vec<Option<BlockDifficulty>>> {
|
|
|
+ let mut ret = Vec::with_capacity(heights.len());
|
|
|
|
|
|
- Ok(order)
|
|
|
- }
|
|
|
+ for height in heights {
|
|
|
+ if let Some(found) = self.difficulty.get(height.to_be_bytes())? {
|
|
|
+ let block_difficulty = deserialize(&found)?;
|
|
|
+ ret.push(Some(block_difficulty));
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if strict {
|
|
|
+ return Err(Error::BlockDifficultyNotFound(*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.
|
|
|
+ pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Block)>> {
|
|
|
+ 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<Vec<(u64, blake3::Hash)>> {
|
|
|
+ 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<Vec<(u64, BlockDifficulty)>> {
|
|
|
+ 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
|
|
|
@@ -373,7 +514,7 @@ impl BlockOrderStore {
|
|
|
let mut key = number;
|
|
|
let mut counter = 0;
|
|
|
while counter <= n {
|
|
|
- if let Some(found) = self.0.get_gt(key.to_be_bytes())? {
|
|
|
+ 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);
|
|
|
@@ -386,10 +527,10 @@ impl BlockOrderStore {
|
|
|
Ok(ret)
|
|
|
}
|
|
|
|
|
|
- /// Fetch the first block hash in the tree, based on the `Ord`
|
|
|
+ /// Fetch the first block hash in the order tree, based on the `Ord`
|
|
|
/// implementation for `Vec<u8>`.
|
|
|
pub fn get_first(&self) -> Result<(u64, blake3::Hash)> {
|
|
|
- let found = match self.0.first()? {
|
|
|
+ let found = match self.order.first()? {
|
|
|
Some(s) => s,
|
|
|
None => return Err(Error::BlockNumberNotFound(0)),
|
|
|
};
|
|
|
@@ -398,39 +539,80 @@ impl BlockOrderStore {
|
|
|
Ok((number, hash))
|
|
|
}
|
|
|
|
|
|
- /// Fetch the last block hash in the tree, based on the `Ord`
|
|
|
+ /// Fetch the last block hash in the order tree, based on the `Ord`
|
|
|
/// implementation for `Vec<u8>`.
|
|
|
pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
|
|
|
- let found = self.0.last()?.unwrap();
|
|
|
+ let found = self.order.last()?.unwrap();
|
|
|
let (number, hash) = parse_u64_key_record(found)?;
|
|
|
|
|
|
Ok((number, hash))
|
|
|
}
|
|
|
|
|
|
- /// Retrieve records count
|
|
|
+ /// Fetch the last record in the difficulty tree, based on the `Ord`
|
|
|
+ /// implementation for `Vec<u8>`. If the tree is empty,
|
|
|
+ /// returns `None`.
|
|
|
+ pub fn get_last_difficulty(&self) -> Result<Option<BlockDifficulty>> {
|
|
|
+ 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 difficulty store, in order.
|
|
|
+ pub fn get_last_n_difficulties(&self, n: usize) -> Result<Vec<BlockDifficulty>> {
|
|
|
+ // Build an iterator to retrieve last N records
|
|
|
+ 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![];
|
|
|
+ for record in records {
|
|
|
+ last_n.insert(0, deserialize(&record?.1)?);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(last_n)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Retrieve store's order tree records count.
|
|
|
pub fn len(&self) -> usize {
|
|
|
- self.0.len()
|
|
|
+ self.order.len()
|
|
|
}
|
|
|
|
|
|
- /// Check if sled contains any records
|
|
|
+ /// Check if store's order tree contains any records.
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
- self.0.is_empty()
|
|
|
+ self.order.is_empty()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// Overlay structure over a [`BlockOrderStore`] instance.
|
|
|
-pub struct BlockOrderStoreOverlay(SledDbOverlayPtr);
|
|
|
+/// Overlay structure over a [`BlockStore`] instance.
|
|
|
+pub struct BlockStoreOverlay(SledDbOverlayPtr);
|
|
|
|
|
|
-impl BlockOrderStoreOverlay {
|
|
|
+impl BlockStoreOverlay {
|
|
|
pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
|
|
|
+ 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 `u64` and block hashes into the store. With sled, the
|
|
|
- /// operation is done as a batch.
|
|
|
+ /// 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<Vec<blake3::Hash>> {
|
|
|
+ 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(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
|
|
|
+ pub fn insert_order(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
|
|
|
if order.len() != hashes.len() {
|
|
|
return Err(Error::InvalidInputLengths)
|
|
|
}
|
|
|
@@ -444,12 +626,52 @@ impl BlockOrderStoreOverlay {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- /// Fetch given order numbers from the overlay.
|
|
|
+ /// 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 {
|
|
|
+ lock.insert(
|
|
|
+ SLED_BLOCK_DIFFICULTY_TREE,
|
|
|
+ &block_difficulty.height.to_be_bytes(),
|
|
|
+ &serialize(block_difficulty),
|
|
|
+ )?;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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<Vec<Option<Block>>> {
|
|
|
+ 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(&self, order: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
|
|
|
+ pub fn get_order(&self, order: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
|
|
|
let mut ret = Vec::with_capacity(order.len());
|
|
|
let lock = self.0.lock().unwrap();
|
|
|
|
|
|
@@ -468,7 +690,7 @@ impl BlockOrderStoreOverlay {
|
|
|
Ok(ret)
|
|
|
}
|
|
|
|
|
|
- /// Fetch the last block hash in the overlay, based on the `Ord`
|
|
|
+ /// Fetch the last block hash in the overlay's order tree, based on the `Ord`
|
|
|
/// implementation for `Vec<u8>`.
|
|
|
pub fn get_last(&self) -> Result<(u64, blake3::Hash)> {
|
|
|
let found = match self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)? {
|
|
|
@@ -480,260 +702,12 @@ impl BlockOrderStoreOverlay {
|
|
|
Ok((number, hash))
|
|
|
}
|
|
|
|
|
|
- /// Check if overlay contains any records
|
|
|
+ /// Check if overlay's order tree contains any records.
|
|
|
pub fn is_empty(&self) -> Result<bool> {
|
|
|
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
|
|
|
-/// ranks, if ever needed.
|
|
|
-#[derive(Debug)]
|
|
|
-pub struct BlockRanks {
|
|
|
- /// Block target rank
|
|
|
- pub target_rank: BigUint,
|
|
|
- /// Height cummulative targets rank
|
|
|
- pub targets_rank: BigUint,
|
|
|
- /// Block hash rank
|
|
|
- pub hash_rank: BigUint,
|
|
|
- /// Height cummulative hashes rank
|
|
|
- pub hashes_rank: BigUint,
|
|
|
-}
|
|
|
-
|
|
|
-impl BlockRanks {
|
|
|
- pub fn new(
|
|
|
- target_rank: BigUint,
|
|
|
- targets_rank: BigUint,
|
|
|
- hash_rank: BigUint,
|
|
|
- hashes_rank: BigUint,
|
|
|
- ) -> Self {
|
|
|
- Self { target_rank, targets_rank, hash_rank, hashes_rank }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Note: Doing all the imports here as this might get obselete if
|
|
|
-// we implemented Encodable/Decodable for num_bigint::BigUint.
|
|
|
-impl darkfi_serial::Encodable for BlockRanks {
|
|
|
- fn encode<S: std::io::Write>(&self, mut s: S) -> std::io::Result<usize> {
|
|
|
- let mut len = 0;
|
|
|
- len += self.target_rank.to_bytes_be().encode(&mut s)?;
|
|
|
- len += self.targets_rank.to_bytes_be().encode(&mut s)?;
|
|
|
- len += self.hash_rank.to_bytes_be().encode(&mut s)?;
|
|
|
- len += self.hashes_rank.to_bytes_be().encode(&mut s)?;
|
|
|
- Ok(len)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl darkfi_serial::Decodable for BlockRanks {
|
|
|
- fn decode<D: std::io::Read>(mut d: D) -> std::io::Result<Self> {
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let target_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let targets_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let hash_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let hashes_rank: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let ret = Self { target_rank, targets_rank, hash_rank, hashes_rank };
|
|
|
- Ok(ret)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/// Auxiliary structure used to keep track of block PoW difficulty information.
|
|
|
-/// Note: we only need height cummulative difficulty, but we also keep its actual
|
|
|
-/// difficulty, so we can verify the sequence and/or know specific block height
|
|
|
-/// difficulty, if ever needed.
|
|
|
-#[derive(Debug)]
|
|
|
-pub struct BlockDifficulty {
|
|
|
- /// Block height number
|
|
|
- pub height: u64,
|
|
|
- /// Block creation timestamp
|
|
|
- pub timestamp: Timestamp,
|
|
|
- /// Height difficulty
|
|
|
- pub difficulty: BigUint,
|
|
|
- /// Height cummulative difficulty (total + height difficulty)
|
|
|
- pub cummulative_difficulty: BigUint,
|
|
|
- /// Block ranks
|
|
|
- pub ranks: BlockRanks,
|
|
|
-}
|
|
|
-
|
|
|
-impl BlockDifficulty {
|
|
|
- pub fn new(
|
|
|
- height: u64,
|
|
|
- timestamp: Timestamp,
|
|
|
- difficulty: BigUint,
|
|
|
- cummulative_difficulty: BigUint,
|
|
|
- ranks: BlockRanks,
|
|
|
- ) -> Self {
|
|
|
- Self { height, timestamp, difficulty, cummulative_difficulty, ranks }
|
|
|
- }
|
|
|
-
|
|
|
- /// Represents the genesis block difficulty
|
|
|
- pub fn genesis(timestamp: Timestamp) -> Self {
|
|
|
- let ranks = BlockRanks::new(
|
|
|
- BigUint::from(0u64),
|
|
|
- BigUint::from(0u64),
|
|
|
- BigUint::from(0u64),
|
|
|
- BigUint::from(0u64),
|
|
|
- );
|
|
|
- BlockDifficulty::new(0, timestamp, BigUint::from(0u64), BigUint::from(0u64), ranks)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Note: Doing all the imports here as this might get obselete if
|
|
|
-// we implemented Encodable/Decodable for num_bigint::BigUint.
|
|
|
-impl darkfi_serial::Encodable for BlockDifficulty {
|
|
|
- fn encode<S: std::io::Write>(&self, mut s: S) -> std::io::Result<usize> {
|
|
|
- let mut len = 0;
|
|
|
- len += self.height.encode(&mut s)?;
|
|
|
- len += self.timestamp.encode(&mut s)?;
|
|
|
- len += self.difficulty.to_bytes_be().encode(&mut s)?;
|
|
|
- len += self.cummulative_difficulty.to_bytes_be().encode(&mut s)?;
|
|
|
- len += self.ranks.encode(&mut s)?;
|
|
|
- Ok(len)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl darkfi_serial::Decodable for BlockDifficulty {
|
|
|
- fn decode<D: std::io::Read>(mut d: D) -> std::io::Result<Self> {
|
|
|
- let height: u64 = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let timestamp: Timestamp = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let difficulty: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let cummulative_difficulty: BigUint = BigUint::from_bytes_be(&bytes);
|
|
|
- let ranks: BlockRanks = darkfi_serial::Decodable::decode(&mut d)?;
|
|
|
- let ret = Self { height, timestamp, difficulty, cummulative_difficulty, ranks };
|
|
|
- Ok(ret)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/// [`BlockDifficulty`] sled tree
|
|
|
-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.
|
|
|
-#[derive(Clone)]
|
|
|
-pub struct BlockDifficultyStore(pub sled::Tree);
|
|
|
-
|
|
|
-impl BlockDifficultyStore {
|
|
|
- /// Opens a new or existing `BlockDifficultyStore` on the given sled database.
|
|
|
- pub fn new(db: &sled::Db) -> Result<Self> {
|
|
|
- let tree = db.open_tree(SLED_BLOCK_DIFFICULTY_TREE)?;
|
|
|
- Ok(Self(tree))
|
|
|
- }
|
|
|
-
|
|
|
- /// 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)?;
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- /// Generate the sled batch corresponding to an insert, 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<sled::Batch> {
|
|
|
- let mut batch = sled::Batch::default();
|
|
|
-
|
|
|
- for block_difficulty in block_difficulties {
|
|
|
- batch.insert(&block_difficulty.height.to_be_bytes(), serialize(block_difficulty));
|
|
|
- }
|
|
|
-
|
|
|
- Ok(batch)
|
|
|
- }
|
|
|
-
|
|
|
- /// Fetch given block height numbers from the block difficulties store.
|
|
|
- /// 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<Vec<Option<BlockDifficulty>>> {
|
|
|
- let mut ret = Vec::with_capacity(heights.len());
|
|
|
-
|
|
|
- for height in heights {
|
|
|
- if let Some(found) = self.0.get(height.to_be_bytes())? {
|
|
|
- let block_difficulty = deserialize(&found)?;
|
|
|
- ret.push(Some(block_difficulty));
|
|
|
- continue
|
|
|
- }
|
|
|
- if strict {
|
|
|
- return Err(Error::BlockDifficultyNotFound(*height))
|
|
|
- }
|
|
|
- ret.push(None);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(ret)
|
|
|
- }
|
|
|
-
|
|
|
- /// Fetch the last record in the tree, based on the `Ord`
|
|
|
- /// implementation for `Vec<u8>`. If the tree is empty,
|
|
|
- /// returns `None`.
|
|
|
- pub fn get_last(&self) -> Result<Option<BlockDifficulty>> {
|
|
|
- let Some(found) = self.0.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<Vec<BlockDifficulty>> {
|
|
|
- // Build an iterator to retrieve last N records
|
|
|
- let records = self.0.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![];
|
|
|
- for record in records {
|
|
|
- last_n.insert(0, deserialize(&record?.1)?);
|
|
|
- }
|
|
|
-
|
|
|
- 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<Vec<(u64, BlockDifficulty)>> {
|
|
|
- let mut block_difficulties = vec![];
|
|
|
-
|
|
|
- for record in self.0.iter() {
|
|
|
- block_difficulties.push(parse_u64_key_record(record.unwrap())?);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(block_difficulties)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/// Overlay structure over a [`BlockDifficultyStore`] instance.
|
|
|
-pub struct BlockDifficultyStoreOverlay(SledDbOverlayPtr);
|
|
|
-
|
|
|
-impl BlockDifficultyStoreOverlay {
|
|
|
- pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
|
|
|
- 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<()> {
|
|
|
- let mut lock = self.0.lock().unwrap();
|
|
|
-
|
|
|
- for block_difficulty in block_difficulties {
|
|
|
- lock.insert(
|
|
|
- SLED_BLOCK_DIFFICULTY_TREE,
|
|
|
- &block_difficulty.height.to_be_bytes(),
|
|
|
- &serialize(block_difficulty),
|
|
|
- )?;
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/// Auxiliary function to append a transaction to a Merkle tree.
|
|
|
pub fn append_tx_to_merkle_tree(tree: &mut MerkleTree, tx: &Transaction) {
|
|
|
let mut buf = [0u8; 64];
|