blockchain: cleanup

This commit is contained in:
aggstam
2023-06-28 18:29:46 +03:00
parent 800adf21d9
commit 97445fca25
6 changed files with 105 additions and 134 deletions

View File

@@ -21,7 +21,7 @@ use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
use crate::{tx::Transaction, Error, Result};
use super::{Header, SledDbOverlayPtr};
use super::{parse_record, Header, SledDbOverlayPtr};
/// Block version number
pub const BLOCK_VERSION: u8 = 1;
@@ -66,7 +66,7 @@ impl Block {
/// Structure representing full block data.
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
pub struct BlockInfo {
/// BlockInfo magic bytes
/// Block magic bytes
pub magic: [u8; 4],
/// Block header data
pub header: Header,
@@ -205,7 +205,7 @@ impl BlockStore {
/// Generate the sled batch corresponding to an insert, so caller
/// can handle the write operation.
/// The blocks are hashed with BLAKE3 and this blockhash is used as
/// The blocks are hashed with BLAKE3 and this block hash 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>)> {
@@ -222,14 +222,14 @@ impl BlockStore {
Ok((batch, ret))
}
/// Check if the blockstore contains a given blockhash.
/// 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 blockstore.
/// Fetch given block hashes from the block store.
/// The resulting vector contains `Option`, which is `Some` if the block
/// was found in the blockstore, 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>>> {
@@ -251,17 +251,14 @@ impl BlockStore {
Ok(ret)
}
/// Retrieve all blocks from the blockstore in the form of a tuple
/// (`blockhash`, `block`).
/// 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() {
let (key, value) = block.unwrap();
let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
let block = deserialize(&value)?;
blocks.push((hash_bytes.into(), block));
blocks.push(parse_record(block.unwrap())?);
}
Ok(blocks)
@@ -278,7 +275,7 @@ impl BlockStoreOverlay {
}
/// Insert a slice of [`Block`] into the overlay.
/// The block are hashed with BLAKE3 and this blockhash is used as
/// The block are hashed with BLAKE3 and this block hash 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>> {
@@ -324,9 +321,9 @@ impl BlockStoreOverlay {
/// Auxiliary structure used to keep track of blocks order.
#[derive(Debug, SerialEncodable, SerialDecodable)]
pub struct BlockOrder {
/// Slot UID
pub slot: u64,
/// Block headerhash of that slot
/// Order number
pub number: u64,
/// Block headerhash of that number
pub block: blake3::Hash,
}
@@ -334,7 +331,7 @@ pub struct BlockOrder {
const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order";
/// The `BlockOrderStore` is a `sled` tree storing the order of the
/// blockchain's slots, where the key is the slot uid, and the value is
/// blockchain's blocks, where the key is the order number, and the value is
/// the blocks' hash. [`BlockStore`] can be queried with this hash.
#[derive(Clone)]
pub struct BlockOrderStore(pub sled::Tree);
@@ -346,51 +343,50 @@ impl BlockOrderStore {
Ok(Self(tree))
}
/// Insert a slice of slots and blockhashes into the store.
pub fn insert(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
let batch = self.insert_batch(slots, hashes)?;
/// 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 slot is used as the key, and the blockhash is used as value.
pub fn insert_batch(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<sled::Batch> {
if slots.len() != hashes.len() {
/// 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, sl) in slots.iter().enumerate() {
batch.insert(&sl.to_be_bytes(), hashes[i].as_bytes());
for (i, number) in order.iter().enumerate() {
batch.insert(&number.to_be_bytes(), hashes[i].as_bytes());
}
Ok(batch)
}
/// Check if the blockorderstore contains a given slot.
pub fn contains(&self, slot: u64) -> Result<bool> {
Ok(self.0.contains_key(slot.to_be_bytes())?)
/// 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 slots from the blockorderstore.
/// The resulting vector contains `Option`, which is `Some` if the slot
/// was found in the blockstore, and otherwise it is `None`, if it has not.
/// 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 slot was not found.
pub fn get(&self, slots: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
let mut ret = Vec::with_capacity(slots.len());
/// 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 slot in slots {
if let Some(found) = self.0.get(slot.to_be_bytes())? {
let hash_bytes: [u8; 32] = found.as_ref().try_into().unwrap();
let hash = blake3::Hash::from(hash_bytes);
ret.push(Some(hash));
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));
} else {
if strict {
return Err(Error::BlockSlotNotFound(*slot))
return Err(Error::BlockNumberNotFound(*number))
}
ret.push(None);
}
@@ -399,38 +395,32 @@ impl BlockOrderStore {
Ok(ret)
}
/// Retrieve all slots from the blockorderstore in the form of a tuple
/// (`slot`, `blockhash`).
/// 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 slots = vec![];
let mut order = vec![];
for slot in self.0.iter() {
let (key, value) = slot.unwrap();
let slot_bytes: [u8; 8] = key.as_ref().try_into().unwrap();
let hash_bytes: [u8; 32] = value.as_ref().try_into().unwrap();
let slot = u64::from_be_bytes(slot_bytes);
let hash = blake3::Hash::from(hash_bytes);
slots.push((slot, hash));
for record in self.0.iter() {
order.push(parse_record(record.unwrap())?);
}
Ok(slots)
Ok(order)
}
/// Fetch n hashes after given slot. In the iteration, if a slot is not
/// found, the iteration stops and the function returns what it has found
/// so far in the `BlockOrderStore`.
pub fn get_after(&self, slot: u64, n: u64) -> Result<Vec<blake3::Hash>> {
/// 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 = slot;
let mut key = number;
let mut counter = 0;
while counter <= n {
if let Some(found) = self.0.get_gt(key.to_be_bytes())? {
let key_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
key = u64::from_be_bytes(key_bytes);
let blockhash = deserialize(&found.1)?;
ret.push(blockhash);
let (number, hash) = parse_record(found)?;
key = number;
ret.push(hash);
counter += 1;
continue
}
@@ -440,33 +430,25 @@ impl BlockOrderStore {
Ok(ret)
}
/// Fetch the first blockhash in the tree, based on the `Ord`
/// 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::SlotNotFound(0)),
None => return Err(Error::BlockNumberNotFound(0)),
};
let (number, hash) = parse_record(found)?;
let slot_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
let hash_bytes: [u8; 32] = found.1.as_ref().try_into().unwrap();
let slot = u64::from_be_bytes(slot_bytes);
let hash = blake3::Hash::from(hash_bytes);
Ok((slot, hash))
Ok((number, hash))
}
/// Fetch the last blockhash in the tree, based on the `Ord`
/// 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_record(found)?;
let slot_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
let hash_bytes: [u8; 32] = found.1.as_ref().try_into().unwrap();
let slot = u64::from_be_bytes(slot_bytes);
let hash = blake3::Hash::from(hash_bytes);
Ok((slot, hash))
Ok((number, hash))
}
/// Retrieve records count
@@ -489,40 +471,39 @@ impl BlockOrderStoreOverlay {
Ok(Self(overlay))
}
/// Insert a slice of slots and blockhashes into the store. With sled, the
/// Insert a slice of `u64` and block hashes into the store. With sled, the
/// operation is done as a batch.
/// The block slot is used as the key, and the blockhash is used as value.
pub fn insert(&self, slots: &[u64], hashes: &[blake3::Hash]) -> Result<()> {
if slots.len() != hashes.len() {
/// 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, sl) in slots.iter().enumerate() {
lock.insert(SLED_BLOCK_ORDER_TREE, &sl.to_be_bytes(), hashes[i].as_bytes())?;
for (i, number) in order.iter().enumerate() {
lock.insert(SLED_BLOCK_ORDER_TREE, &number.to_be_bytes(), hashes[i].as_bytes())?;
}
Ok(())
}
/// Fetch given slots from the overlay.
/// The resulting vector contains `Option`, which is `Some` if the slot
/// 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 slot was not found.
pub fn get(&self, slots: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
let mut ret = Vec::with_capacity(slots.len());
/// 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 slot in slots {
if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &slot.to_be_bytes())? {
let hash_bytes: [u8; 32] = found.as_ref().try_into().unwrap();
let hash = blake3::Hash::from(hash_bytes);
ret.push(Some(hash));
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));
} else {
if strict {
return Err(Error::BlockSlotNotFound(*slot))
return Err(Error::BlockNumberNotFound(*number))
}
ret.push(None);
}
@@ -531,17 +512,13 @@ impl BlockOrderStoreOverlay {
Ok(ret)
}
/// Fetch the last blockhash in the overlay, based on the `Ord`
/// 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 = self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)?.unwrap();
let (number, hash) = parse_record(found)?;
let slot_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
let hash_bytes: [u8; 32] = found.1.as_ref().try_into().unwrap();
let slot = u64::from_be_bytes(slot_bytes);
let hash = blake3::Hash::from(hash_bytes);
Ok((slot, hash))
Ok((number, hash))
}
/// Check if overlay contains any records

View File

@@ -21,7 +21,7 @@ use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
use crate::{util::time::Timestamp, Error, Result};
use super::{block_store::BLOCK_VERSION, SledDbOverlayPtr};
use super::{block_store::BLOCK_VERSION, parse_record, SledDbOverlayPtr};
/// This struct represents a tuple of the form (version, previous, epoch, slot, timestamp, merkle_root).
#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
@@ -52,12 +52,6 @@ impl Header {
Self { version, previous, epoch, slot, timestamp, root }
}
/// Generate the genesis block for provided genesis info.
pub fn genesis_header(genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Self {
let root = MerkleTree::new(100).root(0).unwrap();
Self::new(genesis_data, 0, 0, genesis_ts, root)
}
/// Calculate the header hash
pub fn headerhash(&self) -> blake3::Hash {
blake3::hash(&serialize(self))
@@ -155,10 +149,7 @@ impl HeaderStore {
let mut headers = vec![];
for header in self.0.iter() {
let (key, value) = header.unwrap();
let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
let header = deserialize(&value)?;
headers.push((hash_bytes.into(), header));
headers.push(parse_record(header.unwrap())?);
}
Ok(headers)

View File

@@ -22,24 +22,29 @@ use log::debug;
use sled::Transactional;
use darkfi_sdk::blockchain::Slot;
use darkfi_serial::serialize;
use darkfi_serial::{deserialize, serialize, Decodable};
use crate::{tx::Transaction, Error, Result};
/// Block related definitions and storage implementations
pub mod block_store;
pub use block_store::{
Block, BlockInfo, BlockOrderStore, BlockOrderStoreOverlay, BlockStore, BlockStoreOverlay,
};
/// Header definition and storage implementation
pub mod header_store;
pub use header_store::{Header, HeaderStore, HeaderStoreOverlay};
/// Slots storage implementation
pub mod slot_store;
pub use slot_store::{SlotStore, SlotStoreOverlay};
/// Transactions related storage implementations
pub mod tx_store;
pub use tx_store::{PendingTxOrderStore, PendingTxStore, TxStore, TxStoreOverlay};
/// Contracts and Wasm storage implementations
pub mod contract_store;
pub use contract_store::{
ContractStateStore, ContractStateStoreOverlay, WasmStore, WasmStoreOverlay,
@@ -525,3 +530,13 @@ impl BlockchainOverlay {
Ok(())
}
}
/// Parse a sled record in the form of a tuple (`key`, `value`).
pub fn parse_record<T1: Decodable, T2: Decodable>(
record: (sled::IVec, sled::IVec),
) -> Result<(T1, T2)> {
let key = deserialize(&record.0)?;
let value = deserialize(&record.1)?;
Ok((key, value))
}

View File

@@ -22,7 +22,7 @@ use darkfi_serial::{deserialize, serialize};
use crate::{Error, Result};
use super::SledDbOverlayPtr;
use super::{parse_record, SledDbOverlayPtr};
const SLED_SLOT_TREE: &[u8] = b"_slots";
@@ -93,8 +93,7 @@ impl SlotStore {
let mut slots = vec![];
for slot in self.0.iter() {
let (_, value) = slot.unwrap();
let slot = deserialize(&value)?;
let (_, slot): ([u8; 8], Slot) = parse_record(slot.unwrap())?;
slots.push(slot);
}
@@ -111,9 +110,8 @@ impl SlotStore {
let mut counter = 0;
while counter <= n {
if let Some(found) = self.0.get_gt(key.to_be_bytes())? {
let key_bytes: [u8; 8] = found.0.as_ref().try_into().unwrap();
key = u64::from_be_bytes(key_bytes);
let slot = deserialize(&found.1)?;
let (id, slot) = parse_record(found)?;
key = id;
ret.push(slot);
counter += 1;
continue

View File

@@ -22,7 +22,7 @@ use darkfi_serial::{deserialize, serialize};
use crate::{tx::Transaction, Error, Result};
use super::SledDbOverlayPtr;
use super::{parse_record, SledDbOverlayPtr};
const SLED_TX_TREE: &[u8] = b"_transactions";
const SLED_PENDING_TX_TREE: &[u8] = b"_pending_transactions";
@@ -112,10 +112,7 @@ impl TxStore {
let mut txs = vec![];
for tx in self.0.iter() {
let (key, value) = tx.unwrap();
let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
let tx = deserialize(&value)?;
txs.push((hash_bytes.into(), tx));
txs.push(parse_record(tx.unwrap())?);
}
Ok(txs)
@@ -246,10 +243,8 @@ impl PendingTxStore {
let mut txs = HashMap::new();
for tx in self.0.iter() {
let (key, value) = tx.unwrap();
let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
let tx = deserialize(&value)?;
txs.insert(hash_bytes.into(), tx);
let (key, value) = parse_record(tx.unwrap())?;
txs.insert(key, value);
}
Ok(txs)
@@ -325,12 +320,7 @@ impl PendingTxOrderStore {
let mut txs = vec![];
for tx in self.0.iter() {
let (key, value) = tx.unwrap();
let index_bytes: [u8; 8] = key.as_ref().try_into().unwrap();
let hash_bytes: [u8; 32] = value.as_ref().try_into().unwrap();
let index = u64::from_be_bytes(index_bytes);
let hash = blake3::Hash::from(hash_bytes);
txs.push((index, hash));
txs.push(parse_record(tx.unwrap())?);
}
Ok(txs)

View File

@@ -312,8 +312,8 @@ pub enum Error {
#[error("Block {0} not found in database")]
BlockNotFound(String),
#[error("Block in slot {0} not found in database")]
BlockSlotNotFound(u64),
#[error("Block with order number {0} not found in database")]
BlockNumberNotFound(u64),
#[error("Slot {0} not found in database")]
SlotNotFound(u64),