diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/custom_filters.rs | 12 | ||||
| -rw-r--r-- | src/handlers.rs | 83 | ||||
| -rw-r--r-- | src/main.rs | 2 | ||||
| -rw-r--r-- | src/routes.rs | 80 | ||||
| -rw-r--r-- | src/schema.rs | 95 |
5 files changed, 233 insertions, 39 deletions
diff --git a/src/custom_filters.rs b/src/custom_filters.rs index 8c36d02..0806c6d 100644 --- a/src/custom_filters.rs +++ b/src/custom_filters.rs | |||
| @@ -3,19 +3,27 @@ | |||
| 3 | use std::convert::Infallible; | 3 | use std::convert::Infallible; |
| 4 | use warp::{Filter, Rejection}; | 4 | use warp::{Filter, Rejection}; |
| 5 | 5 | ||
| 6 | use crate::schema::{Block, Db, Transaction}; | 6 | use crate::schema::{Block, Db, Transaction, AuthRequest}; |
| 7 | 7 | ||
| 8 | // Database context for routes | 8 | // Database context for routes |
| 9 | pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clone { | 9 | pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clone { |
| 10 | warp::any().map(move || db.clone()) | 10 | warp::any().map(move || db.clone()) |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | // Accept only json encoded User body and reject big payloads | ||
| 14 | // TODO: find a good limit for this, (=e2482057; 8 char String + rsa pem) <11-04-21, yigit> // | ||
| 15 | pub fn auth_request_json_body() -> impl Filter<Extract = (AuthRequest,), Error = Rejection> + Clone { | ||
| 16 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) | ||
| 17 | } | ||
| 18 | |||
| 13 | // Accept only json encoded Transaction body and reject big payloads | 19 | // Accept only json encoded Transaction body and reject big payloads |
| 20 | // TODO: find a good limit for this <11-04-21, yigit> // | ||
| 14 | pub fn transaction_json_body() -> impl Filter<Extract = (Transaction,), Error = Rejection> + Clone { | 21 | pub fn transaction_json_body() -> impl Filter<Extract = (Transaction,), Error = Rejection> + Clone { |
| 15 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) | 22 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) |
| 16 | } | 23 | } |
| 17 | 24 | ||
| 18 | // Accept only json encoded Transaction body and reject big payloads | 25 | // Accept only json encoded Block body and reject big payloads |
| 26 | // TODO: find a good limit for this <11-04-21, yigit> // | ||
| 19 | pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone { | 27 | pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone { |
| 20 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) | 28 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) |
| 21 | } | 29 | } |
diff --git a/src/handlers.rs b/src/handlers.rs index 856970d..bfd57bc 100644 --- a/src/handlers.rs +++ b/src/handlers.rs | |||
| @@ -1,10 +1,62 @@ | |||
| 1 | /// API handlers, the ends of each filter chain | 1 | /// API handlers, the ends of each filter chain |
| 2 | use log::debug; // this is more useful than debug! learn how to use this | 2 | use log::debug; |
| 3 | use parking_lot::RwLockUpgradableReadGuard; | 3 | use parking_lot::RwLockUpgradableReadGuard; |
| 4 | use serde_json; | ||
| 4 | use std::convert::Infallible; | 5 | use std::convert::Infallible; |
| 5 | use warp::{http::StatusCode, reply}; | 6 | use warp::{http::Response, http::StatusCode, reply}; |
| 6 | 7 | ||
| 7 | use crate::schema::{Block, Db, Transaction}; | 8 | use std::fs; |
| 9 | |||
| 10 | use crate::schema::{AuthRequest, Block, Db, MetuId, Transaction, User}; | ||
| 11 | |||
| 12 | /// POST /register | ||
| 13 | /// Enables a student to introduce themselves to the system | ||
| 14 | /// Can fail | ||
| 15 | pub async fn authenticate_user( | ||
| 16 | request: AuthRequest, | ||
| 17 | db: Db, | ||
| 18 | ) -> Result<impl warp::Reply, warp::Rejection> { | ||
| 19 | let given_id = request.student_id.clone(); | ||
| 20 | |||
| 21 | if let Some(priv_student_id) = MetuId::new(request.student_id) { | ||
| 22 | let userlist = db.users.upgradable_read(); | ||
| 23 | |||
| 24 | if userlist.contains_key(&given_id) { | ||
| 25 | |||
| 26 | let res = Response::builder() | ||
| 27 | .status(StatusCode::BAD_REQUEST) | ||
| 28 | .body("This user is already authenticated"); | ||
| 29 | |||
| 30 | Ok(res) | ||
| 31 | } else { | ||
| 32 | let new_user = User { | ||
| 33 | user_id: priv_student_id, | ||
| 34 | public_key: request.public_key, | ||
| 35 | balance: 0, | ||
| 36 | }; | ||
| 37 | |||
| 38 | let user_json = serde_json::to_string(&new_user).unwrap(); | ||
| 39 | |||
| 40 | fs::write(format!("users/{}.guy", new_user.user_id), user_json).unwrap(); | ||
| 41 | |||
| 42 | let mut userlist = RwLockUpgradableReadGuard::upgrade(userlist); | ||
| 43 | userlist.insert(given_id, new_user); | ||
| 44 | // TODO: signature of the public key, please <11-04-21, yigit> // | ||
| 45 | |||
| 46 | let res = Response::builder() | ||
| 47 | .status(StatusCode::CREATED) | ||
| 48 | .body("Ready to use Gradecoin"); | ||
| 49 | |||
| 50 | Ok(res) | ||
| 51 | } | ||
| 52 | } else { | ||
| 53 | let res = Response::builder() | ||
| 54 | .status(StatusCode::BAD_REQUEST) | ||
| 55 | .body("This user cannot have a gradecoin account"); | ||
| 56 | |||
| 57 | Ok(res) | ||
| 58 | } | ||
| 59 | } | ||
| 8 | 60 | ||
| 9 | /// GET /transaction | 61 | /// GET /transaction |
| 10 | /// Returns JSON array of transactions | 62 | /// Returns JSON array of transactions |
| @@ -28,16 +80,11 @@ pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> { | |||
| 28 | /// Cannot fail | 80 | /// Cannot fail |
| 29 | /// Mostly around for debug purposes | 81 | /// Mostly around for debug purposes |
| 30 | pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { | 82 | pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { |
| 31 | debug!("list all blocks"); | 83 | debug!("list all block"); |
| 32 | |||
| 33 | let mut result = Vec::new(); | ||
| 34 | let blocks = db.blockchain.read(); | ||
| 35 | 84 | ||
| 36 | for block in blocks.iter() { | 85 | let block = db.blockchain.read(); |
| 37 | result.push(block); | ||
| 38 | } | ||
| 39 | 86 | ||
| 40 | Ok(reply::with_status(reply::json(&result), StatusCode::OK)) | 87 | Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) |
| 41 | } | 88 | } |
| 42 | 89 | ||
| 43 | /// POST /transaction | 90 | /// POST /transaction |
| @@ -70,7 +117,7 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply, | |||
| 70 | let pending_transactions = db.pending_transactions.upgradable_read(); | 117 | let pending_transactions = db.pending_transactions.upgradable_read(); |
| 71 | let blockchain = db.blockchain.upgradable_read(); | 118 | let blockchain = db.blockchain.upgradable_read(); |
| 72 | 119 | ||
| 73 | // TODO: check 1, new_block.transaction_list from pending_transactions pool? <07-04-21, yigit> // | 120 | // check 1, new_block.transaction_list from pending_transactions pool? <07-04-21, yigit> // |
| 74 | for transaction_hash in new_block.transaction_list.iter() { | 121 | for transaction_hash in new_block.transaction_list.iter() { |
| 75 | if !pending_transactions.contains_key(transaction_hash) { | 122 | if !pending_transactions.contains_key(transaction_hash) { |
| 76 | return Ok(StatusCode::BAD_REQUEST); | 123 | return Ok(StatusCode::BAD_REQUEST); |
| @@ -81,7 +128,17 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply, | |||
| 81 | // assume it is for now | 128 | // assume it is for now |
| 82 | 129 | ||
| 83 | let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); | 130 | let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); |
| 84 | blockchain.push(new_block); | 131 | |
| 132 | let block_json = serde_json::to_string(&new_block).unwrap(); | ||
| 133 | |||
| 134 | // let mut file = File::create(format!("{}.block", new_block.timestamp.timestamp())).unwrap(); | ||
| 135 | fs::write( | ||
| 136 | format!("blocks/{}.block", new_block.timestamp.timestamp()), | ||
| 137 | block_json, | ||
| 138 | ) | ||
| 139 | .unwrap(); | ||
| 140 | |||
| 141 | *blockchain = new_block; | ||
| 85 | 142 | ||
| 86 | let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); | 143 | let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); |
| 87 | pending_transactions.clear(); | 144 | pending_transactions.clear(); |
diff --git a/src/main.rs b/src/main.rs index 7ef2597..373223c 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | use std::env; | 1 | use std::env; |
| 2 | use warp::Filter; | 2 | use warp::Filter; |
| 3 | 3 | ||
| 4 | mod custom_filters; | ||
| 5 | mod handlers; | 4 | mod handlers; |
| 5 | mod custom_filters; | ||
| 6 | mod routes; | 6 | mod routes; |
| 7 | mod schema; | 7 | mod schema; |
| 8 | // mod validators; | 8 | // mod validators; |
diff --git a/src/routes.rs b/src/routes.rs index 95138e6..9f0adc5 100644 --- a/src/routes.rs +++ b/src/routes.rs | |||
| @@ -7,11 +7,21 @@ use crate::schema::Db; | |||
| 7 | /// Root, all routes combined | 7 | /// Root, all routes combined |
| 8 | pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | 8 | pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { |
| 9 | transaction_list(db.clone()) | 9 | transaction_list(db.clone()) |
| 10 | .or(register_user(db.clone())) | ||
| 10 | .or(transaction_propose(db.clone())) | 11 | .or(transaction_propose(db.clone())) |
| 11 | .or(block_propose(db.clone())) | 12 | .or(block_propose(db.clone())) |
| 12 | .or(block_list(db.clone())) | 13 | .or(block_list(db.clone())) |
| 13 | } | 14 | } |
| 14 | 15 | ||
| 16 | /// POST /register warp route | ||
| 17 | pub fn register_user(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | ||
| 18 | warp::path!("register") | ||
| 19 | .and(warp::post()) | ||
| 20 | .and(custom_filters::auth_request_json_body()) | ||
| 21 | .and(custom_filters::with_db(db)) | ||
| 22 | .and_then(handlers::authenticate_user) | ||
| 23 | } | ||
| 24 | |||
| 15 | /// GET /transaction warp route | 25 | /// GET /transaction warp route |
| 16 | pub fn transaction_list(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | 26 | pub fn transaction_list(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { |
| 17 | warp::path!("transaction") | 27 | warp::path!("transaction") |
| @@ -50,13 +60,13 @@ pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Reject | |||
| 50 | mod tests { | 60 | mod tests { |
| 51 | use super::*; | 61 | use super::*; |
| 52 | 62 | ||
| 53 | use chrono::prelude::*; | 63 | // use chrono::prelude::*; |
| 54 | use parking_lot::RwLock; | 64 | // use parking_lot::RwLock; |
| 55 | use std::sync::Arc; | 65 | // use std::sync::Arc; |
| 56 | use warp::http::StatusCode; | 66 | use warp::http::StatusCode; |
| 57 | 67 | ||
| 58 | use crate::schema; | 68 | use crate::schema; |
| 59 | use crate::schema::{Block, Transaction}; | 69 | use crate::schema::{AuthRequest, Block, Transaction}; |
| 60 | 70 | ||
| 61 | /// Create a mock database to be used in tests | 71 | /// Create a mock database to be used in tests |
| 62 | fn mocked_db() -> Db { | 72 | fn mocked_db() -> Db { |
| @@ -72,7 +82,7 @@ mod tests { | |||
| 72 | }, | 82 | }, |
| 73 | ); | 83 | ); |
| 74 | 84 | ||
| 75 | db.blockchain.write().push(Block { | 85 | *db.blockchain.write() = Block { |
| 76 | transaction_list: vec![ | 86 | transaction_list: vec![ |
| 77 | "old_transaction_hash_1".to_owned(), | 87 | "old_transaction_hash_1".to_owned(), |
| 78 | "old_transaction_hash_2".to_owned(), | 88 | "old_transaction_hash_2".to_owned(), |
| @@ -81,11 +91,27 @@ mod tests { | |||
| 81 | nonce: "not_a_thing_yet".to_owned(), | 91 | nonce: "not_a_thing_yet".to_owned(), |
| 82 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | 92 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), |
| 83 | hash: "not_a_thing_yet".to_owned(), | 93 | hash: "not_a_thing_yet".to_owned(), |
| 84 | }); | 94 | }; |
| 85 | 95 | ||
| 86 | db | 96 | db |
| 87 | } | 97 | } |
| 88 | 98 | ||
| 99 | /// Create a mock user that is allowed to be in gradecoin to be used in tests | ||
| 100 | fn priviliged_mocked_user() -> AuthRequest { | ||
| 101 | AuthRequest { | ||
| 102 | student_id: String::from("e254275"), | ||
| 103 | public_key: "NOT IMPLEMENTED".to_owned(), | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Create a mock user that is NOT allowed to be in gradecoin to be used in tests | ||
| 108 | fn unpriviliged_mocked_user() -> AuthRequest { | ||
| 109 | AuthRequest { | ||
| 110 | student_id: String::from("foobarbaz"), | ||
| 111 | public_key: "NOT IMPLEMENTED".to_owned(), | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 89 | /// Create a mock transaction to be used in tests | 115 | /// Create a mock transaction to be used in tests |
| 90 | fn mocked_transaction() -> Transaction { | 116 | fn mocked_transaction() -> Transaction { |
| 91 | Transaction { | 117 | Transaction { |
| @@ -134,7 +160,7 @@ mod tests { | |||
| 134 | 160 | ||
| 135 | assert_eq!(res.status(), StatusCode::OK); | 161 | assert_eq!(res.status(), StatusCode::OK); |
| 136 | 162 | ||
| 137 | let expected_json_body = r#"[{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":"not_a_thing_yet","timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}]"#; | 163 | let expected_json_body = r#"{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":"not_a_thing_yet","timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}"#; |
| 138 | assert_eq!(res.body(), expected_json_body); | 164 | assert_eq!(res.body(), expected_json_body); |
| 139 | } | 165 | } |
| 140 | 166 | ||
| @@ -175,6 +201,46 @@ mod tests { | |||
| 175 | assert_eq!(db.pending_transactions.read().len(), 2); | 201 | assert_eq!(db.pending_transactions.read().len(), 2); |
| 176 | } | 202 | } |
| 177 | 203 | ||
| 204 | /// TEST a POST request to /transaction, an endpoint that exists | ||
| 205 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
| 206 | /// Should accept the json request, create a new user and | ||
| 207 | /// add it to the user hashmap in the db | ||
| 208 | #[tokio::test] | ||
| 209 | async fn post_register_priviliged_user() { | ||
| 210 | let db = mocked_db(); | ||
| 211 | let filter = consensus_routes(db.clone()); | ||
| 212 | |||
| 213 | let res = warp::test::request() | ||
| 214 | .method("POST") | ||
| 215 | .json(&priviliged_mocked_user()) | ||
| 216 | .path("/register") | ||
| 217 | .reply(&filter) | ||
| 218 | .await; | ||
| 219 | |||
| 220 | println!("{:?}", res.body()); | ||
| 221 | assert_eq!(res.status(), StatusCode::CREATED); | ||
| 222 | assert_eq!(db.users.read().len(), 1); | ||
| 223 | } | ||
| 224 | /// TEST a POST request to /transaction, an endpoint that exists | ||
| 225 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
| 226 | /// Should NOT accept the json request | ||
| 227 | #[tokio::test] | ||
| 228 | async fn post_register_unpriviliged_user() { | ||
| 229 | let db = mocked_db(); | ||
| 230 | let filter = consensus_routes(db.clone()); | ||
| 231 | |||
| 232 | let res = warp::test::request() | ||
| 233 | .method("POST") | ||
| 234 | .json(&unpriviliged_mocked_user()) | ||
| 235 | .path("/register") | ||
| 236 | .reply(&filter) | ||
| 237 | .await; | ||
| 238 | |||
| 239 | println!("{:?}", res.body()); | ||
| 240 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); | ||
| 241 | assert_eq!(db.users.read().len(), 0); | ||
| 242 | } | ||
| 243 | |||
| 178 | /// Test a POST request to /transaction, a resource that exists with a longer than expected | 244 | /// Test a POST request to /transaction, a resource that exists with a longer than expected |
| 179 | /// payload | 245 | /// payload |
| 180 | /// https://tools.ietf.org/html/rfc7231#section-6.5.11 | 246 | /// https://tools.ietf.org/html/rfc7231#section-6.5.11 |
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 @@ | |||
| 1 | use chrono::NaiveDateTime; | 1 | use chrono::{NaiveDate, NaiveDateTime}; |
| 2 | use lazy_static::lazy_static; | ||
| 2 | use parking_lot::RwLock; | 3 | use parking_lot::RwLock; |
| 3 | use serde::{Deserialize, Serialize}; | 4 | use serde::{Deserialize, Serialize}; |
| 4 | use std::collections::HashMap; | 5 | use std::collections::{HashMap, HashSet}; |
| 6 | use std::fmt; | ||
| 7 | use std::fs; | ||
| 5 | use std::sync::Arc; | 8 | use 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 |
| 22 | pub fn create_database() -> Db { | 19 | pub 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)] |
| 27 | pub struct Db { | 26 | pub 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 | ||
| 34 | impl Db { | 34 | impl 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)] |
| 45 | pub struct Transaction { | 46 | pub 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 | ||
| 70 | impl 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)] | ||
| 84 | pub 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)] | ||
| 92 | pub struct MetuId { | ||
| 93 | id: String, | ||
| 94 | } | ||
| 95 | |||
| 96 | #[derive(Serialize, Deserialize, Debug)] | ||
| 97 | pub struct AuthRequest { | ||
| 98 | pub student_id: String, | ||
| 99 | pub public_key: String, | ||
| 100 | } | ||
| 101 | |||
| 102 | lazy_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 | |||
| 116 | impl fmt::Display for MetuId { | ||
| 117 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 118 | write!(f, "{}", self.id) | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | impl 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> // |
