aboutsummaryrefslogtreecommitdiffstats
path: root/src/db.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.rs')
-rw-r--r--src/db.rs159
1 files changed, 159 insertions, 0 deletions
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 @@
1//! # Global Database representation
2//!
3//! [`Db::blockchain`] is just the last block that was mined.
4//! All the blocks are written to disk as text files whenever they are accepted.
5//!
6//! [`Db::pending_transactions`] is the in memory representation of the waiting transactions.
7//! Every user can have only one outstanding transaction at any given time.
8//!
9//! [`Db::users`] is the in memory representation of the users,
10//! with their public keys, `metu_ids` and gradecoin balances.
11use crate::block::{Block, Fingerprint, Id, Transaction};
12use crate::student::{MetuId, User, UserAtRest};
13use log::debug;
14use parking_lot::RwLock;
15use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc};
16
17#[derive(Debug, Clone, Default)]
18pub struct Db {
19 pub blockchain: Arc<RwLock<Block>>,
20 pub pending_transactions: Arc<RwLock<HashMap<Id, Transaction>>>,
21 pub users: Arc<RwLock<HashMap<Fingerprint, User>>>,
22 // TODO: metu_ids or approved_users or something, metu_id struct <11-04-22, yigit> //
23}
24
25impl Db {
26 pub fn new() -> Self {
27 fs::create_dir_all("blocks").unwrap();
28 fs::create_dir_all("users").unwrap();
29 let mut db = Db::default();
30 if let Some(block_path) = last_block_content() {
31 db.populate_with_last_block(block_path);
32 }
33
34 if let Ok(users_path) = read_users() {
35 db.populate_with_users(users_path);
36 }
37
38 let users: HashMap<Fingerprint, User> = get_friendly_users();
39
40 Db {
41 blockchain: Arc::new(RwLock::new(Block::default())),
42 pending_transactions: Arc::new(RwLock::new(HashMap::new())),
43 users: Arc::new(RwLock::new(users)),
44 }
45 }
46
47 fn populate_with_last_block(&mut self, path: String) {
48 debug!("Populating db with the latest block {}", path);
49 let file = fs::read(path).unwrap();
50 let json = std::str::from_utf8(&file).unwrap();
51 let block: Block = serde_json::from_str(json).unwrap();
52 *self.blockchain.write() = block;
53 }
54
55 fn populate_with_users(&mut self, files: Vec<PathBuf>) {
56 for fs in files {
57 if let Ok(file_content) = fs::read(fs) {
58 let json =
59 String::from_utf8(file_content).expect("we have written a malformed user file");
60 let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap();
61
62 debug!("Populating db with user: {:?}", user_at_rest);
63 self.users
64 .write()
65 .insert(user_at_rest.fingerprint, user_at_rest.user);
66 }
67 }
68 }
69}
70
71fn last_block_content() -> Option<String> {
72 let blocks = read_block_name().unwrap();
73
74 if blocks.is_empty() {
75 return None;
76 }
77
78 let last_block = blocks[0].to_str().unwrap();
79 let mut last_block = parse_block(last_block);
80 let mut last_block_index = 0;
81
82 for (index, block) in blocks.iter().enumerate() {
83 let block = block.to_str().unwrap();
84 let block = parse_block(block);
85 if block > last_block {
86 last_block = block;
87 last_block_index = index;
88 }
89 }
90 return Some(blocks[last_block_index].to_str().unwrap().parse().unwrap());
91}
92
93fn read_block_name() -> io::Result<Vec<PathBuf>> {
94 let entries = fs::read_dir("./blocks")?
95 .map(|res| res.map(|e| e.path()))
96 .collect::<Result<Vec<_>, io::Error>>()?;
97
98 Ok(entries)
99}
100
101fn parse_block(path: &str) -> u64 {
102 let end_pos = path.find(".block").unwrap();
103 let block_str = path[9..end_pos].to_string();
104 let block_u64: u64 = block_str.parse().unwrap();
105 block_u64
106}
107
108fn read_users() -> io::Result<Vec<PathBuf>> {
109 let entries = fs::read_dir("./users")?
110 .map(|res| res.map(|e| e.path()))
111 .collect::<Result<Vec<_>, io::Error>>()?;
112
113 Ok(entries)
114}
115
116fn get_friendly_users() -> HashMap<Fingerprint, User> {
117 let mut users: HashMap<Fingerprint, User> = HashMap::new();
118
119 users.insert(
120 "cde48537ca2c28084ff560826d0e6388b7c57a51497a6cb56f397289e52ff41b".to_owned(),
121 User {
122 user_id: MetuId::new("friend_1".to_owned(), "not_used".to_owned()).unwrap(),
123 public_key: "not_used".to_owned(),
124 balance: 70,
125 is_bot: true,
126 },
127 );
128
129 users.insert(
130 "a1a38b5bae5866d7d998a9834229ec2f9db7a4fc8fb6f58b1115a96a446875ff".to_owned(),
131 User {
132 user_id: MetuId::new("friend_2".to_owned(), "not_used".to_owned()).unwrap(),
133 public_key: "not_used".to_owned(),
134 balance: 20,
135 is_bot: true,
136 },
137 );
138
139 users.insert(
140 "4e048fd2a62f1307866086e803e9be43f78a702d5df10831fbf434e7663ae0e7".to_owned(),
141 User {
142 user_id: MetuId::new("friend_4".to_owned(), "not_used".to_owned()).unwrap(),
143 public_key: "not_used".to_owned(),
144 balance: 120,
145 is_bot: true,
146 },
147 );
148
149 users.insert(
150 "60e77101e76950a9b1830fa107fd2f8fc545255b3e0f14b6a7797cf9ee005f07".to_owned(),
151 User {
152 user_id: MetuId::new("friend_4".to_owned(), "not_used".to_owned()).unwrap(),
153 public_key: "not_used".to_owned(),
154 balance: 40,
155 is_bot: true,
156 },
157 );
158 users
159}