tests & various routines

This commit is contained in:
NikVolf 2016-10-15 17:48:06 +03:00
parent add9c72817
commit aa2e1f64d0
3 changed files with 111 additions and 5 deletions

View File

@ -53,6 +53,14 @@ impl Block {
let hashes = self.transactions.iter().map(Transaction::hash).collect::<Vec<H256>>();
merkle_root(&hashes)
}
pub fn transactions(&self) -> &[Transaction] {
&self.transactions[..]
}
pub fn header(&self) -> &BlockHeader {
&self.block_header
}
}
#[cfg(test)]

View File

@ -25,15 +25,26 @@ const _COL_RESERVED6: u32 = 10;
const DB_VERSION: u32 = 1;
pub trait Store {
/// resolves hash by block number
fn block_hash(&self, number: u64) -> Option<H256>;
/// resolves header bytes by block reference (number/hash)
fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes>;
/// resolves list of block transactions by block reference (number/hash)
fn block_transactions(&self, block_ref: BlockRef) -> Vec<H256>;
/// resolves transaction body bytes by transaction hash
fn transaction_bytes(&self, hash: &H256) -> Option<Bytes>;
/// resolves serialized transaction info by transaction hash
fn transaction(&self, hash: &H256) -> Option<chain::Transaction>;
/// resolves deserialized block body by block reference (number/hash)
fn block(&self, block_ref: BlockRef) -> Option<chain::Block>;
/// insert block in the storage
fn insert_block(&self, block: &chain::Block) -> Result<(), Error>;
}
pub struct Storage {
@ -42,10 +53,10 @@ pub struct Storage {
#[derive(Debug)]
pub enum MetaError {
NoVersion,
UnsupportedVersion,
}
#[derive(Debug)]
/// Database error
pub enum Error {
/// Rocksdb error
@ -74,10 +85,12 @@ fn u64_key(num: u64) -> [u8; 8] {
result
}
const KEY_VERSION: &'static[u8] = b"version";
impl Storage {
// new storage at the selected path
// if no directory exists, it will be created
/// new storage at the selected path
/// if no directory exists, it will be created
pub fn new<P: AsRef<Path>>(path: P) -> Result<Storage, Error> {
try!(fs::create_dir_all(path.as_ref()));
let cfg = DatabaseConfig::with_columns(Some(COL_COUNT));
@ -93,14 +106,24 @@ impl Storage {
Err(Error::Meta(MetaError::UnsupportedVersion))
}
},
_ => Err(Error::Meta(MetaError::NoVersion))
_ => {
let mut meta_transaction = db.transaction();
let mut ver_val = [0u8; 4];
LittleEndian::write_u32(&mut ver_val, DB_VERSION);
meta_transaction.put(Some(COL_META), KEY_VERSION, &ver_val);
try!(db.write(meta_transaction));
Ok(Storage { database: db, })
}
}
}
/// is invoked on database non-fatal query errors
fn db_error(&self, msg: String) {
println!("Low-level database error: {}", &msg);
}
/// get the value of the key in the database
/// if the key is not present, reports non-fatal error and returns nothing
fn get(&self, col: u32, key: &[u8]) -> Option<Bytes> {
let res = self.database.get(Some(col), key);
match res {
@ -112,6 +135,8 @@ impl Storage {
}
}
/// resolves hash for the block reference (which can be referenced by number or
/// by hash)
fn resolve_hash(&self, block_ref: BlockRef) -> Option<H256> {
match block_ref {
BlockRef::Number(n) => self.block_hash(n),
@ -119,6 +144,7 @@ impl Storage {
}
}
/// loads block transaction list by the provided block hash
fn block_transactions_by_hash(&self, h: &H256) -> Vec<H256> {
self.get(COL_BLOCK_TRANSACTIONS, &**h)
.unwrap_or(Vec::new())
@ -160,7 +186,7 @@ impl Store for Storage {
match chain::Transaction::deserialize(&mut reader) {
Ok(tx) => Some(tx),
Err(e) => {
self.db_error(format!("Error deserializing header, possible db corruption ({:?})", e));
self.db_error(format!("Error deserializing transaction, possible db corruption ({:?})", e));
None
}
}
@ -180,8 +206,75 @@ impl Store for Storage {
})
)
}
fn insert_block(&self, block: &chain::Block) -> Result<(), Error> {
let mut transaction = self.database.transaction();
let tx_space = block.transactions().len() * 32;
let block_hash = block.hash();
let mut tx_refs = Vec::with_capacity(tx_space);
for tx in block.transactions() {
let tx_hash = tx.hash();
tx_refs.extend(&*tx_hash);
let mut tx_stream = serialization::Stream::new();
tx.serialize(&mut tx_stream);
transaction.put(Some(COL_TRANSACTIONS), &*tx_hash, tx_stream.out().as_slice());
}
transaction.put(Some(COL_BLOCK_TRANSACTIONS), &*block_hash, &tx_refs);
let mut header_stream = serialization::Stream::new();
block.header().serialize(&mut header_stream);
transaction.put(Some(COL_BLOCK_HEADERS), &*block_hash, header_stream.out().as_slice());
try!(self.database.write(transaction));
Ok(())
}
fn transaction(&self, hash: &H256) -> Option<chain::Transaction> {
}
}
#[cfg(test)]
mod tests {
use super::{Storage, Store};
use devtools::RandomTempPath;
use chain::Block;
use super::super::BlockRef;
#[test]
fn open_store() {
let path = RandomTempPath::create_dir();
assert!(Storage::new(path.as_path()).is_ok());
}
#[test]
fn insert_block() {
let path = RandomTempPath::create_dir();
let store = Storage::new(path.as_path()).unwrap();
let block: Block = "01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad27b9137190000000000190760b278fe7b8565fda3b968b918d5fd997f993b23674c0af3b6fde300b38f33a5914ce6ed5b1b01e32f570201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704e6ed5b1b014effffffff0100f2052a01000000434104b68a50eaa0287eff855189f949c1c6e5f58b37c88231373d8a59809cbae83059cc6469d65c665ccfd1cfeb75c6e8e19413bba7fbff9bc762419a76d87b16086eac000000000100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".into();
store.insert_block(&block).unwrap();
let loaded_block = store.block(BlockRef::Hash(block.hash())).unwrap();
assert_eq!(loaded_block.hash(), block.hash());
}
#[test]
fn load_transaction() {
let path = RandomTempPath::create_dir();
let store = Storage::new(path.as_path()).unwrap();
let block: Block = "01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad27b9137190000000000190760b278fe7b8565fda3b968b918d5fd997f993b23674c0af3b6fde300b38f33a5914ce6ed5b1b01e32f570201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704e6ed5b1b014effffffff0100f2052a01000000434104b68a50eaa0287eff855189f949c1c6e5f58b37c88231373d8a59809cbae83059cc6469d65c665ccfd1cfeb75c6e8e19413bba7fbff9bc762419a76d87b16086eac000000000100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".into();
let tx1 = block.transactions()[0].hash();
store.insert_block(&block).unwrap();
let loaded_transaction = store.transaction_bytes(&tx1).unwrap();
assert_eq!(vec![0u8; 0], loaded_transaction);
}
}

View File

@ -21,6 +21,11 @@ pub struct Stream {
}
impl Stream {
/// New stream
pub fn new() -> Self {
Stream { buffer: Vec::new() }
}
/// Serializes the struct and appends it to the end of stream.
pub fn append(&mut self, t: &Serializable) -> &mut Self {
t.serialize(self);