blockchain: Implement some general query functions.

This commit is contained in:
parazyd
2022-04-21 14:01:38 +02:00
parent 0299b45de3
commit 1ae263b4de
5 changed files with 143 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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

View File

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