blockchain/block_store: unified all trees into a single store structure

This commit is contained in:
skoupidi
2024-04-01 16:40:12 +03:00
parent ea93623ff8
commit 34dd60a9b7
5 changed files with 385 additions and 452 deletions

View File

@@ -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)]
);

View File

@@ -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<Self> {
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<Vec<blake3::Hash>> {
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<blake3::Hash>)> {
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<bool> {
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<Vec<Option<Block>>> {
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<Vec<(blake3::Hash, Block)>> {
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<Self> {
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<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)
}
/// 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<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)
}
}
/// 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<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.
/// 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>>> {
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<Vec<(u64, blake3::Hash)>> {
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<Vec<blake3::Hash>> {
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<u8>`.
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<u8>`.
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<Self> {
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<Vec<Option<blake3::Hash>>> {
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<u8>`.
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<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
@@ -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<Self> {
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<Vec<blake3::Hash>> {
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<blake3::Hash>)> {
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<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)
}
/// 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<sled::Batch> {
pub fn insert_batch_difficulty(
&self,
block_difficulties: &[BlockDifficulty],
) -> Result<sled::Batch> {
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<bool> {
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<bool> {
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<Vec<Option<Block>>> {
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<Vec<Option<blake3::Hash>>> {
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<Vec<Option<BlockDifficulty>>> {
pub fn get_difficulty(
&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())? {
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<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
/// it has found so far in the `BlockOrderStore`.
pub fn get_after(&self, number: u64, n: u64) -> Result<Vec<blake3::Hash>> {
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<u8>`.
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<u8>`.
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<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) };
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 block difficulties store, in order.
pub fn get_last_n(&self, n: usize) -> Result<Vec<BlockDifficulty>> {
/// 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.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<Vec<(u64, BlockDifficulty)>> {
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<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 [`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<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_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<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_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();
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<u8>`.
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<bool> {
Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?)
}
}
/// Auxiliary function to append a transaction to a Merkle tree.

View File

@@ -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<Self> {
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<bool> {
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<Vec<BlockInfo>> {
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<Vec<BlockInfo>> {
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<BlockDifficulty> {
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<bool> {
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<Vec<BlockInfo>> {
let order = self.order.get_all()?;
let order = self.blocks.get_all_order()?;
let order: Vec<blake3::Hash> = 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<bool> {
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<bool> {
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 })))
}
}

View File

@@ -99,7 +99,7 @@ impl PoWModule {
let mut timestamps = RingBuffer::<Timestamp, BUF_SIZE>::new();
let mut difficulties = RingBuffer::<BigUint, BUF_SIZE>::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

View File

@@ -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];