From 426e83ec6fba028692bed334803ae9d3a645cb18 Mon Sep 17 00:00:00 2001 From: Yigit Sever Date: Mon, 11 Apr 2022 19:01:42 +0300 Subject: [WIP] Spring cleaning --- src/db.rs | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/db.rs (limited to 'src/db.rs') diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..bf094ab --- /dev/null +++ b/src/db.rs @@ -0,0 +1,159 @@ +//! # Global Database representation +//! +//! [`Db::blockchain`] is just the last block that was mined. +//! All the blocks are written to disk as text files whenever they are accepted. +//! +//! [`Db::pending_transactions`] is the in memory representation of the waiting transactions. +//! Every user can have only one outstanding transaction at any given time. +//! +//! [`Db::users`] is the in memory representation of the users, +//! with their public keys, `metu_ids` and gradecoin balances. +use crate::block::{Block, Fingerprint, Id, Transaction}; +use crate::student::{MetuId, User, UserAtRest}; +use log::debug; +use parking_lot::RwLock; +use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc}; + +#[derive(Debug, Clone, Default)] +pub struct Db { + pub blockchain: Arc>, + pub pending_transactions: Arc>>, + pub users: Arc>>, + // TODO: metu_ids or approved_users or something, metu_id struct <11-04-22, yigit> // +} + +impl Db { + pub fn new() -> Self { + fs::create_dir_all("blocks").unwrap(); + fs::create_dir_all("users").unwrap(); + let mut db = Db::default(); + if let Some(block_path) = last_block_content() { + db.populate_with_last_block(block_path); + } + + if let Ok(users_path) = read_users() { + db.populate_with_users(users_path); + } + + let users: HashMap = get_friendly_users(); + + Db { + blockchain: Arc::new(RwLock::new(Block::default())), + pending_transactions: Arc::new(RwLock::new(HashMap::new())), + users: Arc::new(RwLock::new(users)), + } + } + + fn populate_with_last_block(&mut self, path: String) { + debug!("Populating db with the latest block {}", path); + let file = fs::read(path).unwrap(); + let json = std::str::from_utf8(&file).unwrap(); + let block: Block = serde_json::from_str(json).unwrap(); + *self.blockchain.write() = block; + } + + fn populate_with_users(&mut self, files: Vec) { + for fs in files { + if let Ok(file_content) = fs::read(fs) { + let json = + String::from_utf8(file_content).expect("we have written a malformed user file"); + let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap(); + + debug!("Populating db with user: {:?}", user_at_rest); + self.users + .write() + .insert(user_at_rest.fingerprint, user_at_rest.user); + } + } + } +} + +fn last_block_content() -> Option { + let blocks = read_block_name().unwrap(); + + if blocks.is_empty() { + return None; + } + + let last_block = blocks[0].to_str().unwrap(); + let mut last_block = parse_block(last_block); + let mut last_block_index = 0; + + for (index, block) in blocks.iter().enumerate() { + let block = block.to_str().unwrap(); + let block = parse_block(block); + if block > last_block { + last_block = block; + last_block_index = index; + } + } + return Some(blocks[last_block_index].to_str().unwrap().parse().unwrap()); +} + +fn read_block_name() -> io::Result> { + let entries = fs::read_dir("./blocks")? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + + Ok(entries) +} + +fn parse_block(path: &str) -> u64 { + let end_pos = path.find(".block").unwrap(); + let block_str = path[9..end_pos].to_string(); + let block_u64: u64 = block_str.parse().unwrap(); + block_u64 +} + +fn read_users() -> io::Result> { + let entries = fs::read_dir("./users")? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + + Ok(entries) +} + +fn get_friendly_users() -> HashMap { + let mut users: HashMap = HashMap::new(); + + users.insert( + "cde48537ca2c28084ff560826d0e6388b7c57a51497a6cb56f397289e52ff41b".to_owned(), + User { + user_id: MetuId::new("friend_1".to_owned(), "not_used".to_owned()).unwrap(), + public_key: "not_used".to_owned(), + balance: 70, + is_bot: true, + }, + ); + + users.insert( + "a1a38b5bae5866d7d998a9834229ec2f9db7a4fc8fb6f58b1115a96a446875ff".to_owned(), + User { + user_id: MetuId::new("friend_2".to_owned(), "not_used".to_owned()).unwrap(), + public_key: "not_used".to_owned(), + balance: 20, + is_bot: true, + }, + ); + + users.insert( + "4e048fd2a62f1307866086e803e9be43f78a702d5df10831fbf434e7663ae0e7".to_owned(), + User { + user_id: MetuId::new("friend_4".to_owned(), "not_used".to_owned()).unwrap(), + public_key: "not_used".to_owned(), + balance: 120, + is_bot: true, + }, + ); + + users.insert( + "60e77101e76950a9b1830fa107fd2f8fc545255b3e0f14b6a7797cf9ee005f07".to_owned(), + User { + user_id: MetuId::new("friend_4".to_owned(), "not_used".to_owned()).unwrap(), + public_key: "not_used".to_owned(), + balance: 40, + is_bot: true, + }, + ); + users +} -- cgit v1.2.3-70-g09d2