parity-zcash/db/src/test_storage.rs

179 lines
5.3 KiB
Rust

//! Test storage
use super::{
BlockRef, Store, Error, BestBlock, BlockLocation, BlockInsertedChain, BlockProvider,
BlockStapler, TransactionMetaProvider, TransactionProvider,
};
use chain::{self, Block, RepresentH256};
use primitives::hash::H256;
use serialization;
use chain::bytes::Bytes;
use std::mem::replace;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use parking_lot::RwLock;
use transaction_meta::TransactionMeta;
#[derive(Default)]
pub struct TestStorage {
data: RwLock<TestData>,
}
#[derive(Default)]
struct TestData {
best_block: Option<BestBlock>,
blocks: HashMap<H256, chain::Block>,
heights: HashMap<u32, H256>,
hashes: HashMap<H256, u32>,
}
impl TestStorage {
fn resolve_hash(&self, block_ref: BlockRef) -> Option<H256> {
match block_ref {
BlockRef::Number(n) => self.block_hash(n),
BlockRef::Hash(h) => Some(h),
}
}
pub fn with_blocks(blocks: &[chain::Block]) -> Self {
let blocks_len = blocks.len();
let storage = TestStorage::default();
{
let mut data = storage.data.write();
if blocks_len != 0 {
data.best_block = Some(BestBlock {
number: blocks_len as u32 - 1,
hash: blocks[blocks_len - 1].hash(),
});
for (idx, block) in blocks.iter().enumerate() {
let hash = block.hash();
data.blocks.insert(hash.clone(), block.clone());
data.heights.insert(idx as u32, hash.clone());
data.hashes.insert(hash, idx as u32);
}
}
}
storage
}
pub fn with_genesis_block() -> Self {
let genesis_block: Block = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into();
TestStorage::with_blocks(&vec![genesis_block])
}
}
impl BlockProvider for TestStorage {
fn block_number(&self, hash: &H256) -> Option<u32> {
let data = self.data.read();
data.hashes.get(hash).cloned()
}
fn block_hash(&self, number: u32) -> Option<H256> {
let data = self.data.read();
data.heights.get(&number).cloned()
}
fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes> {
let data = self.data.read();
self.resolve_hash(block_ref)
.and_then(|ref h| data.blocks.get(h))
.map(|ref block| serialization::serialize(block.header()))
}
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
let data = self.data.read();
self.resolve_hash(block_ref)
.and_then(|ref h| data.blocks.get(h))
.map(|ref block| block.transactions().iter().map(|tx| tx.hash()).collect())
.unwrap_or(Vec::new())
}
fn block_transactions(&self, block_ref: BlockRef) -> Vec<chain::Transaction> {
self.block(block_ref)
.map(|b| b.transactions().iter().cloned().collect())
.unwrap_or(Vec::new())
}
fn block(&self, block_ref: BlockRef) -> Option<chain::Block> {
let data = self.data.read();
self.resolve_hash(block_ref)
.and_then(|ref h| data.blocks.get(h))
.cloned()
}
}
impl BlockStapler for TestStorage {
fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error> {
let hash = block.hash();
let mut data = self.data.write();
match data.blocks.entry(hash.clone()) {
Entry::Occupied(mut entry) => {
replace(entry.get_mut(), block.clone());
return Ok(BlockInsertedChain::Main);
},
Entry::Vacant(entry) => {
entry.insert(block.clone());
},
}
match data.best_block {
Some(BestBlock { number: best_block_number, hash: _ }) => {
data.best_block = Some(BestBlock {
number: best_block_number + 1,
hash: hash.clone(),
});
data.heights.insert(best_block_number + 1, hash.clone());
data.hashes.insert(hash, best_block_number + 1);
},
None => {
data.best_block = Some(BestBlock {
number: 0,
hash: hash.clone(),
});
data.heights.insert(0, hash.clone());
data.hashes.insert(hash, 0);
},
}
Ok(BlockInsertedChain::Main)
}
// supports only main chain in test storage
fn accepted_location(&self, header: &chain::BlockHeader) -> Option<BlockLocation> {
match self.best_block() {
None => Some(BlockLocation::Main(0)),
Some(ref best) if best.hash == header.previous_header_hash => Some(BlockLocation::Main(best.number + 1)),
_ => None
}
}
}
impl TransactionProvider for TestStorage {
fn transaction_bytes(&self, hash: &H256) -> Option<Bytes> {
self.transaction(hash).map(|tx| serialization::serialize(&tx))
}
fn transaction(&self, hash: &H256) -> Option<chain::Transaction> {
let data = self.data.read();
data.blocks.iter().flat_map(|(_, b)| b.transactions())
.find(|ref tx| tx.hash() == *hash)
.cloned()
}
}
impl TransactionMetaProvider for TestStorage {
// just spawns new meta so far, use real store for proper tests
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
self.transaction(hash).map(|tx| TransactionMeta::new(0, tx.outputs.len()))
}
}
impl Store for TestStorage {
fn best_block(&self) -> Option<BestBlock> {
self.data.read().best_block.clone()
}
}