diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-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 | ||||
-rwxr-xr-x | tester.sh | 21 |
8 files changed, 257 insertions, 42 deletions
@@ -281,6 +281,7 @@ name = "gradecoin" | |||
281 | version = "0.1.0" | 281 | version = "0.1.0" |
282 | dependencies = [ | 282 | dependencies = [ |
283 | "chrono", | 283 | "chrono", |
284 | "lazy_static", | ||
284 | "log", | 285 | "log", |
285 | "parking_lot", | 286 | "parking_lot", |
286 | "pretty_env_logger", | 287 | "pretty_env_logger", |
@@ -14,7 +14,8 @@ chrono = { version = "0.4.10", features = ["serde"] } | |||
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | pretty_env_logger = "0.3.1" | 15 | pretty_env_logger = "0.3.1" |
16 | parking_lot = "0.10.0" | 16 | parking_lot = "0.10.0" |
17 | serde_json = "1.0.59" | ||
18 | lazy_static = "1.4.0" | ||
17 | 19 | ||
18 | [dev-dependencies] | 20 | [dev-dependencies] |
19 | serde_json = "1.0.44" | 21 | serde_test = "1.0.117" |
20 | serde_test = "1.0.104" | ||
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> // |
@@ -2,6 +2,26 @@ | |||
2 | 2 | ||
3 | ## When in doubt just write a shell script | 3 | ## When in doubt just write a shell script |
4 | 4 | ||
5 | ## new registration request | ||
6 | ## should fail because foobar is not a student | ||
7 | curl --request POST \ | ||
8 | --header 'Content-Type: application/json' \ | ||
9 | --data '{ | ||
10 | "student_id": "foobar", | ||
11 | "public_key": "not_implemented_yet" | ||
12 | }' \ | ||
13 | http://localhost:8080/register | ||
14 | |||
15 | ## new registration request | ||
16 | ## should fail because foobar is not a student | ||
17 | curl --request POST \ | ||
18 | --header 'Content-Type: application/json' \ | ||
19 | --data '{ | ||
20 | "student_id": "e254275", | ||
21 | "public_key": "not_implemented_yet" | ||
22 | }' \ | ||
23 | http://localhost:8080/register | ||
24 | |||
5 | ## new transaction | 25 | ## new transaction |
6 | curl --request POST \ | 26 | curl --request POST \ |
7 | --header 'Content-Type: application/json' \ | 27 | --header 'Content-Type: application/json' \ |
@@ -72,4 +92,3 @@ curl localhost:8080/transaction | |||
72 | 92 | ||
73 | printf "\n\nShould have only one block\n\n" | 93 | printf "\n\nShould have only one block\n\n" |
74 | curl localhost:8080/block | 94 | curl localhost:8080/block |
75 | |||