summaryrefslogtreecommitdiffstats
path: root/src/schema.rs
diff options
context:
space:
mode:
authorYigit Sever2021-04-11 21:39:18 +0300
committerYigit Sever2021-04-12 00:03:23 +0300
commit518a99a132707ba0e2572b24ca18f6b9606d7334 (patch)
treec7cbe183f088b286903389f58743b1e5688119c5 /src/schema.rs
parent217398c52c68b3b73454d3e9f66c85b5a1638f3f (diff)
downloadgradecoin-518a99a132707ba0e2572b24ca18f6b9606d7334.tar.gz
gradecoin-518a99a132707ba0e2572b24ca18f6b9606d7334.tar.bz2
gradecoin-518a99a132707ba0e2572b24ca18f6b9606d7334.zip
Implement User handling and authentication
New struct: User, corresponds to a student Blocks and users are persistent (written to a text file) PostgreSQL would've been overkill, we have 30 students AuthRequest is the representation for incoming register requests and User is the inner representation Students who are enrolled to the class are hardcoded, only they can register new accounts There are two new tests, one checks if a priviliged (=enrolled) user can create an account and the other checks if a unpriviliged one cannot There are quick verbose error messages that I'm not married to, might move on to something better honestly There's nothing stopping a malicious user to pre-register everyone with mock public keys and effectively lock everyone out, what's a good secret we can use?
Diffstat (limited to 'src/schema.rs')
-rw-r--r--src/schema.rs95
1 files changed, 79 insertions, 16 deletions
diff --git a/src/schema.rs b/src/schema.rs
index 556e625..909b5cd 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -1,41 +1,42 @@
1use chrono::NaiveDateTime; 1use chrono::{NaiveDate, NaiveDateTime};
2use lazy_static::lazy_static;
2use parking_lot::RwLock; 3use parking_lot::RwLock;
3use serde::{Deserialize, Serialize}; 4use serde::{Deserialize, Serialize};
4use std::collections::HashMap; 5use std::collections::{HashMap, HashSet};
6use std::fmt;
7use std::fs;
5use std::sync::Arc; 8use std::sync::Arc;
6 9
7// use crate::validators; 10// use crate::validators;
8 11
9// In memory data structure 12/// We need persistence for blocks and users, not so much for transactions
10// Two approaches here 13/// There are around 30 students, a full fledged database would be an overkill (for next year?)
11// 1. Db is a type pub type Db = Arc<RwLock<Vec<Ledger>>>; Ledger is a struct, we wrap the ledger 14/// Pending transactions are held in memory, these are cleared with every new block
12// with arc + mutex in ledger() to access transactions we need to unwrap blocks as well, vice 15/// Only the last block is held in memory, every block is written to a file
13// versa 16/// Users are held in memory and they're also backed up to text files
14//
15// 2. Db is a struct attributes are wrapped we can offload ::new() to it's member method blocks and
16// transactions are accessible separately, which is the biggest pro
17//
18// 3. use an actual database (for blockchain and users this makes the most sense tbh but pending
19// transactions are perfectly fine in memory)
20 17
21/// Creates a new database 18/// Creates a new database connection
22pub fn create_database() -> Db { 19pub fn create_database() -> Db {
20 fs::create_dir_all("blocks").unwrap();
21 fs::create_dir_all("users").unwrap();
23 Db::new() 22 Db::new()
24} 23}
25 24
26#[derive(Debug, Clone)] 25#[derive(Debug, Clone)]
27pub struct Db { 26pub struct Db {
28 // heh. also https://doc.rust-lang.org/std/collections/struct.LinkedList.html says Vec is generally faster 27 // heh. also https://doc.rust-lang.org/std/collections/struct.LinkedList.html says Vec is generally faster
29 pub blockchain: Arc<RwLock<Vec<Block>>>, 28 pub blockchain: Arc<RwLock<Block>>,
30 // every proposer can have _one_ pending transaction, a way to enforce this, String is proposer identifier 29 // every proposer can have _one_ pending transaction, a way to enforce this, String is proposer identifier
31 pub pending_transactions: Arc<RwLock<HashMap<String, Transaction>>>, 30 pub pending_transactions: Arc<RwLock<HashMap<String, Transaction>>>,
31 pub users: Arc<RwLock<HashMap<String, User>>>,
32} 32}
33 33
34impl Db { 34impl Db {
35 fn new() -> Self { 35 fn new() -> Self {
36 Db { 36 Db {
37 blockchain: Arc::new(RwLock::new(Vec::new())), 37 blockchain: Arc::new(RwLock::new(Block::new())),
38 pending_transactions: Arc::new(RwLock::new(HashMap::new())), 38 pending_transactions: Arc::new(RwLock::new(HashMap::new())),
39 users: Arc::new(RwLock::new(HashMap::new())),
39 } 40 }
40 } 41 }
41} 42}
@@ -43,6 +44,7 @@ impl Db {
43/// A transaction between `source` and `target` that moves `amount` 44/// A transaction between `source` and `target` that moves `amount`
44#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 45#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
45pub struct Transaction { 46pub struct Transaction {
47 // TODO: new field by <11-04-21, yigit> //
46 pub source: String, 48 pub source: String,
47 pub target: String, 49 pub target: String,
48 pub amount: i32, 50 pub amount: i32,
@@ -65,5 +67,66 @@ pub struct Block {
65 pub hash: String, // future proof'd baby 67 pub hash: String, // future proof'd baby
66} 68}
67 69
70impl Block {
71 /// Genesis block
72 pub fn new() -> Block {
73 Block {
74 transaction_list: vec![],
75 nonce: String::from(""),
76 timestamp: NaiveDate::from_ymd(2021, 04, 11).and_hms(20, 45, 00),
77 hash: String::from(""),
78 }
79 }
80}
81
82/// Or simply a Student
83#[derive(Serialize, Deserialize, Debug)]
84pub struct User {
85 pub user_id: MetuId,
86 pub public_key: String,
87 pub balance: i32,
88}
89
90/// The values will be hard coded so MetuId::new() can accept/reject values based on that
91#[derive(Serialize, Deserialize, Debug)]
92pub struct MetuId {
93 id: String,
94}
95
96#[derive(Serialize, Deserialize, Debug)]
97pub struct AuthRequest {
98 pub student_id: String,
99 pub public_key: String,
100}
101
102lazy_static! {
103 static ref OUR_STUDENTS: HashSet<&'static str> = {
104 [
105 "e254275", "e223687", "e211024", "e209888", "e223725", "e209362", "e209898", "e230995",
106 "e223743", "e223747", "e223749", "e223751", "e188126", "e209913", "e203608", "e233013",
107 "e216982", "e217185", "e223780", "e194931", "e223783", "e254550", "e217203", "e217477",
108 "e223786", "e231060", "e223795",
109 ]
110 .iter()
111 .cloned()
112 .collect()
113 };
114}
115
116impl fmt::Display for MetuId {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "{}", self.id)
119 }
120}
121
122impl MetuId {
123 pub fn new(id: String) -> Option<Self> {
124 if OUR_STUDENTS.contains(&*id) {
125 Some(MetuId { id: id })
126 } else {
127 None
128 }
129 }
130}
68 131
69// TODO: write schema tests using the original repo <09-04-21, yigit> // 132// TODO: write schema tests using the original repo <09-04-21, yigit> //