diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/block.rs | 1 | ||||
-rw-r--r-- | src/config.rs | 10 | ||||
-rw-r--r-- | src/db.rs | 18 | ||||
-rw-r--r-- | src/handlers.rs | 119 | ||||
-rw-r--r-- | src/main.rs | 20 | ||||
-rw-r--r-- | src/routes.rs | 25 | ||||
-rw-r--r-- | src/student.rs | 8 |
7 files changed, 112 insertions, 89 deletions
diff --git a/src/block.rs b/src/block.rs index e707779..1a61d72 100644 --- a/src/block.rs +++ b/src/block.rs | |||
@@ -9,7 +9,6 @@ | |||
9 | //! Users are held in memory and they're also backed up to text files | 9 | //! Users are held in memory and they're also backed up to text files |
10 | use chrono::{NaiveDate, NaiveDateTime}; | 10 | use chrono::{NaiveDate, NaiveDateTime}; |
11 | use serde::{Deserialize, Serialize}; | 11 | use serde::{Deserialize, Serialize}; |
12 | use std::{string::String, vec::Vec}; | ||
13 | 12 | ||
14 | pub type Fingerprint = String; | 13 | pub type Fingerprint = String; |
15 | pub type Id = String; | 14 | pub type Id = String; |
diff --git a/src/config.rs b/src/config.rs index 28c63be..112fb3c 100644 --- a/src/config.rs +++ b/src/config.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | //! # Configuration | 1 | //! # Configuration |
2 | //! | 2 | //! |
3 | //! This module holds the data structures for network configuration. | 3 | //! This module holds the data structures for network configuration. |
4 | use serde::{Deserialize, Serialize}; | ||
5 | use log::{error, info}; | 4 | use log::{error, info}; |
5 | use serde::{Deserialize, Serialize}; | ||
6 | 6 | ||
7 | /// Configuration for a single network | 7 | /// Configuration for a single network |
8 | #[derive(Debug, Serialize, Deserialize, Clone, Default)] | 8 | #[derive(Debug, Serialize, Deserialize, Clone, Default)] |
@@ -29,7 +29,7 @@ pub struct Config { | |||
29 | 29 | ||
30 | /// Valid blocks should have this many transactions | 30 | /// Valid blocks should have this many transactions |
31 | pub block_transaction_count: u8, | 31 | pub block_transaction_count: u8, |
32 | 32 | ||
33 | /// How many zero hexadecimal characters should a correct hash start with? | 33 | /// How many zero hexadecimal characters should a correct hash start with? |
34 | pub hash_zeros: u8, | 34 | pub hash_zeros: u8, |
35 | 35 | ||
@@ -58,15 +58,15 @@ impl Config { | |||
58 | error!("Cannot read config file: {}", filename); | 58 | error!("Cannot read config file: {}", filename); |
59 | error!("Error: {:?}", e); | 59 | error!("Error: {:?}", e); |
60 | return None; | 60 | return None; |
61 | }, | 61 | } |
62 | }; | 62 | }; |
63 | let config : Config = match serde_yaml::from_reader(file) { | 63 | let config: Config = match serde_yaml::from_reader(file) { |
64 | Ok(c) => c, | 64 | Ok(c) => c, |
65 | Err(e) => { | 65 | Err(e) => { |
66 | error!("Cannot parse config file: {}", filename); | 66 | error!("Cannot parse config file: {}", filename); |
67 | error!("Error: {:?}", e); | 67 | error!("Error: {:?}", e); |
68 | return None; | 68 | return None; |
69 | }, | 69 | } |
70 | }; | 70 | }; |
71 | // File closes automatically when it goes out of scope. | 71 | // File closes automatically when it goes out of scope. |
72 | info!("Config file read successfully: {}", filename); | 72 | info!("Config file read successfully: {}", filename); |
@@ -9,9 +9,9 @@ | |||
9 | //! [`Db::users`] is the in memory representation of the users, | 9 | //! [`Db::users`] is the in memory representation of the users, |
10 | //! with their public keys, `metu_ids` and gradecoin balances. | 10 | //! with their public keys, `metu_ids` and gradecoin balances. |
11 | use crate::block::{Block, Fingerprint, Id, Transaction}; | 11 | use crate::block::{Block, Fingerprint, Id, Transaction}; |
12 | use crate::student::{MetuId, User, UserAtRest}; | ||
13 | use crate::config::Config; | 12 | use crate::config::Config; |
14 | use log::debug; | 13 | use crate::student::{MetuId, User, UserAtRest}; |
14 | use log::info; | ||
15 | use parking_lot::RwLock; | 15 | use parking_lot::RwLock; |
16 | use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc}; | 16 | use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc}; |
17 | 17 | ||
@@ -32,7 +32,7 @@ impl Db { | |||
32 | // Load bots | 32 | // Load bots |
33 | let users: HashMap<Fingerprint, User> = get_friendly_users(); | 33 | let users: HashMap<Fingerprint, User> = get_friendly_users(); |
34 | 34 | ||
35 | // Read the list of users who can register | 35 | // Load the list of users who can register |
36 | let preapproved_users = read_approved_users(&config.preapproved_users); | 36 | let preapproved_users = read_approved_users(&config.preapproved_users); |
37 | 37 | ||
38 | let mut db = Db { | 38 | let mut db = Db { |
@@ -43,21 +43,21 @@ impl Db { | |||
43 | preapproved_users, | 43 | preapproved_users, |
44 | }; | 44 | }; |
45 | 45 | ||
46 | // Read blocks | 46 | // Load the latest block, continue from where we left off |
47 | if let Some(block_path) = last_block_content(&db.config.name) { | 47 | if let Some(block_path) = last_block_content(&db.config.name) { |
48 | db.populate_with_last_block(block_path); | 48 | db.populate_with_last_block(block_path); |
49 | } | 49 | } |
50 | 50 | ||
51 | // Read users | 51 | // Load the users that had registered themselves |
52 | if let Ok(users_path) = read_users(&db.config.name) { | 52 | if let Ok(users_path) = read_users(&db.config.name) { |
53 | db.populate_with_users(users_path); | 53 | db.populate_with_users(users_path); |
54 | } | 54 | } |
55 | 55 | ||
56 | return db; | 56 | db |
57 | } | 57 | } |
58 | 58 | ||
59 | fn populate_with_last_block(&mut self, path: String) { | 59 | fn populate_with_last_block(&mut self, path: String) { |
60 | debug!("Populating db with the latest block {}", path); | 60 | info!("Populating db with the latest block {}", path); |
61 | let file = fs::read(path).unwrap(); | 61 | let file = fs::read(path).unwrap(); |
62 | let json = std::str::from_utf8(&file).unwrap(); | 62 | let json = std::str::from_utf8(&file).unwrap(); |
63 | let block: Block = serde_json::from_str(json).unwrap(); | 63 | let block: Block = serde_json::from_str(json).unwrap(); |
@@ -71,7 +71,7 @@ impl Db { | |||
71 | String::from_utf8(file_content).expect("we have written a malformed user file"); | 71 | String::from_utf8(file_content).expect("we have written a malformed user file"); |
72 | let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap(); | 72 | let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap(); |
73 | 73 | ||
74 | debug!("Populating db with user: {:?}", user_at_rest); | 74 | info!("Populating db with user: {:?}", user_at_rest); |
75 | self.users | 75 | self.users |
76 | .write() | 76 | .write() |
77 | .insert(user_at_rest.fingerprint, user_at_rest.user); | 77 | .insert(user_at_rest.fingerprint, user_at_rest.user); |
@@ -122,7 +122,7 @@ fn read_block_name(config_name: &str) -> io::Result<Vec<PathBuf>> { | |||
122 | } | 122 | } |
123 | 123 | ||
124 | fn parse_block(path: &str) -> u64 { | 124 | fn parse_block(path: &str) -> u64 { |
125 | let start_pos = path.rfind("/").unwrap() + 1; | 125 | let start_pos = path.rfind('/').unwrap() + 1; |
126 | let end_pos = path.find(".block").unwrap(); | 126 | let end_pos = path.find(".block").unwrap(); |
127 | let block_str = path[start_pos..end_pos].to_string(); | 127 | let block_str = path[start_pos..end_pos].to_string(); |
128 | let block_u64: u64 = block_str.parse().unwrap(); | 128 | let block_u64: u64 = block_str.parse().unwrap(); |
diff --git a/src/handlers.rs b/src/handlers.rs index 01e2c4f..9e1bae1 100644 --- a/src/handlers.rs +++ b/src/handlers.rs | |||
@@ -30,7 +30,7 @@ use crate::PRIVATE_KEY; | |||
30 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; | 30 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; |
31 | 31 | ||
32 | #[derive(Serialize, Debug)] | 32 | #[derive(Serialize, Debug)] |
33 | struct GradeCoinResponse { | 33 | struct UserFeedback { |
34 | res: ResponseType, | 34 | res: ResponseType, |
35 | message: String, | 35 | message: String, |
36 | } | 36 | } |
@@ -97,7 +97,7 @@ pub async fn authenticate_user( | |||
97 | request: InitialAuthRequest, | 97 | request: InitialAuthRequest, |
98 | db: Db, | 98 | db: Db, |
99 | ) -> Result<impl warp::Reply, warp::Rejection> { | 99 | ) -> Result<impl warp::Reply, warp::Rejection> { |
100 | debug!("POST /register, authenticate_user() is handling"); | 100 | debug!("[{}] New user registration attempt", db.config.name); |
101 | 101 | ||
102 | // In essence PEM files are just base64 encoded versions of the DER encoded data. | 102 | // In essence PEM files are just base64 encoded versions of the DER encoded data. |
103 | // ~tls.mbed.org | 103 | // ~tls.mbed.org |
@@ -113,7 +113,7 @@ pub async fn authenticate_user( | |||
113 | &request.key, err | 113 | &request.key, err |
114 | ); | 114 | ); |
115 | 115 | ||
116 | let res_json = warp::reply::json(&GradeCoinResponse { | 116 | let res_json = warp::reply::json(&UserFeedback { |
117 | res: ResponseType::Error, | 117 | res: ResponseType::Error, |
118 | message: format!( | 118 | message: format!( |
119 | "\"key\" field of initial auth request was not base64 encoded: {}, {}", | 119 | "\"key\" field of initial auth request was not base64 encoded: {}, {}", |
@@ -134,7 +134,7 @@ pub async fn authenticate_user( | |||
134 | err, &key_ciphertext | 134 | err, &key_ciphertext |
135 | ); | 135 | ); |
136 | 136 | ||
137 | let res_json = warp::reply::json(&GradeCoinResponse { | 137 | let res_json = warp::reply::json(&UserFeedback { |
138 | res: ResponseType::Error, | 138 | res: ResponseType::Error, |
139 | message: "Failed to decrypt the 'key_ciphertext' field of the auth request" | 139 | message: "Failed to decrypt the 'key_ciphertext' field of the auth request" |
140 | .to_owned(), | 140 | .to_owned(), |
@@ -153,7 +153,7 @@ pub async fn authenticate_user( | |||
153 | &request.iv, err | 153 | &request.iv, err |
154 | ); | 154 | ); |
155 | 155 | ||
156 | let res_json = warp::reply::json(&GradeCoinResponse { | 156 | let res_json = warp::reply::json(&UserFeedback { |
157 | res: ResponseType::Error, | 157 | res: ResponseType::Error, |
158 | message: format!( | 158 | message: format!( |
159 | "\"iv\" field of initial auth request was not base64 encoded: {}, {}", | 159 | "\"iv\" field of initial auth request was not base64 encoded: {}, {}", |
@@ -174,11 +174,11 @@ pub async fn authenticate_user( | |||
174 | &temp_key, &request.iv, err | 174 | &temp_key, &request.iv, err |
175 | ); | 175 | ); |
176 | 176 | ||
177 | let res_json = warp::reply::json(&GradeCoinResponse { | 177 | let res_json = warp::reply::json(&UserFeedback { |
178 | res: ResponseType::Error, | 178 | res: ResponseType::Error, |
179 | message: format!( | 179 | message: format!( |
180 | "Could not create a cipher from given 'temp_key': {:?} and 'IV': {}", | 180 | "Could not create a cipher from given 'temp_key': {:?} and 'IV': {}, {}", |
181 | &temp_key, &request.iv | 181 | &temp_key, &request.iv, err |
182 | ), | 182 | ), |
183 | }); | 183 | }); |
184 | 184 | ||
@@ -195,7 +195,7 @@ pub async fn authenticate_user( | |||
195 | &request.c, err | 195 | &request.c, err |
196 | ); | 196 | ); |
197 | 197 | ||
198 | let res_json = warp::reply::json(&GradeCoinResponse { | 198 | let res_json = warp::reply::json(&UserFeedback { |
199 | res: ResponseType::Error, | 199 | res: ResponseType::Error, |
200 | message: format!( | 200 | message: format!( |
201 | "\"c\" field of initial auth request was not base64 encoded: {}, {}", | 201 | "\"c\" field of initial auth request was not base64 encoded: {}, {}", |
@@ -218,7 +218,7 @@ pub async fn authenticate_user( | |||
218 | &buf, err | 218 | &buf, err |
219 | ); | 219 | ); |
220 | 220 | ||
221 | let res_json = warp::reply::json(&GradeCoinResponse { | 221 | let res_json = warp::reply::json(&UserFeedback { |
222 | res: ResponseType::Error, | 222 | res: ResponseType::Error, |
223 | message: "Failed to decrypt the 'c' field of the auth request, 'iv' and 'k_temp' were valid so far though" | 223 | message: "Failed to decrypt the 'c' field of the auth request, 'iv' and 'k_temp' were valid so far though" |
224 | .to_owned(), | 224 | .to_owned(), |
@@ -237,7 +237,7 @@ pub async fn authenticate_user( | |||
237 | &auth_plaintext, err | 237 | &auth_plaintext, err |
238 | ); | 238 | ); |
239 | 239 | ||
240 | let res_json = warp::reply::json(&GradeCoinResponse { | 240 | let res_json = warp::reply::json(&UserFeedback { |
241 | res: ResponseType::Error, | 241 | res: ResponseType::Error, |
242 | message: "P_AR couldn't get converted to UTF-8, please check your encoding" | 242 | message: "P_AR couldn't get converted to UTF-8, please check your encoding" |
243 | .to_owned(), | 243 | .to_owned(), |
@@ -256,7 +256,7 @@ pub async fn authenticate_user( | |||
256 | &utf8_auth_plaintext, err | 256 | &utf8_auth_plaintext, err |
257 | ); | 257 | ); |
258 | 258 | ||
259 | let res_json = warp::reply::json(&GradeCoinResponse { | 259 | let res_json = warp::reply::json(&UserFeedback { |
260 | res: ResponseType::Error, | 260 | res: ResponseType::Error, |
261 | message: "The P_AR JSON did not serialize correctly, did it include all 3 fields 'student_id', 'passwd' and 'public_key'?".to_owned(), | 261 | message: "The P_AR JSON did not serialize correctly, did it include all 3 fields 'student_id', 'passwd' and 'public_key'?".to_owned(), |
262 | }); | 262 | }); |
@@ -273,7 +273,7 @@ pub async fn authenticate_user( | |||
273 | "Someone tried to auth with invalid credentials: {} {}", | 273 | "Someone tried to auth with invalid credentials: {} {}", |
274 | &request.student_id, &request.passwd | 274 | &request.student_id, &request.passwd |
275 | ); | 275 | ); |
276 | let res_json = warp::reply::json(&GradeCoinResponse { | 276 | let res_json = warp::reply::json(&UserFeedback { |
277 | res: ResponseType::Error, | 277 | res: ResponseType::Error, |
278 | message: | 278 | message: |
279 | "The credentials given ('student_id', 'passwd') cannot hold a Gradecoin account" | 279 | "The credentials given ('student_id', 'passwd') cannot hold a Gradecoin account" |
@@ -289,7 +289,8 @@ pub async fn authenticate_user( | |||
289 | 289 | ||
290 | for (_, user) in userlist.iter() { | 290 | for (_, user) in userlist.iter() { |
291 | if user.user_id == privileged_student_id { | 291 | if user.user_id == privileged_student_id { |
292 | let res_json = warp::reply::json(&GradeCoinResponse { | 292 | debug!("{} attempted to authenticate again", user.user_id); |
293 | let res_json = warp::reply::json(&UserFeedback { | ||
293 | res: ResponseType::Error, | 294 | res: ResponseType::Error, |
294 | message: | 295 | message: |
295 | "This user is already authenticated, do you think this is a mistake? Contact me" | 296 | "This user is already authenticated, do you think this is a mistake? Contact me" |
@@ -302,7 +303,7 @@ pub async fn authenticate_user( | |||
302 | 303 | ||
303 | // We're using this as the validator instead of anything reasonable | 304 | // We're using this as the validator instead of anything reasonable |
304 | if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() { | 305 | if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() { |
305 | let res_json = warp::reply::json(&GradeCoinResponse { | 306 | let res_json = warp::reply::json(&UserFeedback { |
306 | res: ResponseType::Error, | 307 | res: ResponseType::Error, |
307 | message: "The RSA 'public_key' in 'P_AR' is not in valid PEM format".to_owned(), | 308 | message: "The RSA 'public_key' in 'P_AR' is not in valid PEM format".to_owned(), |
308 | }); | 309 | }); |
@@ -319,7 +320,7 @@ pub async fn authenticate_user( | |||
319 | is_bot: false, | 320 | is_bot: false, |
320 | }; | 321 | }; |
321 | 322 | ||
322 | debug!("NEW USER: {:?}", &new_user); | 323 | warn!("A new user has authenticated: {}", &new_user.user_id); |
323 | 324 | ||
324 | // save the user to disk | 325 | // save the user to disk |
325 | let user_at_rest_json = serde_json::to_string(&UserAtRest { | 326 | let user_at_rest_json = serde_json::to_string(&UserAtRest { |
@@ -335,13 +336,14 @@ pub async fn authenticate_user( | |||
335 | 336 | ||
336 | fs::write( | 337 | fs::write( |
337 | format!("users/{}/{}.guy", db.config.name, new_user.user_id), | 338 | format!("users/{}/{}.guy", db.config.name, new_user.user_id), |
338 | user_at_rest_json | 339 | user_at_rest_json, |
339 | ).unwrap(); | 340 | ) |
341 | .unwrap(); | ||
340 | 342 | ||
341 | let mut userlist = db.users.write(); | 343 | let mut userlist = db.users.write(); |
342 | userlist.insert(fingerprint.clone(), new_user); | 344 | userlist.insert(fingerprint.clone(), new_user); |
343 | 345 | ||
344 | let res_json = warp::reply::json(&GradeCoinResponse { | 346 | let res_json = warp::reply::json(&UserFeedback { |
345 | res: ResponseType::Success, | 347 | res: ResponseType::Success, |
346 | message: format!( | 348 | message: format!( |
347 | "You have authenticated to use Gradecoin with identifier {}", | 349 | "You have authenticated to use Gradecoin with identifier {}", |
@@ -396,7 +398,7 @@ pub async fn propose_block( | |||
396 | new_block.transaction_list.len(), | 398 | new_block.transaction_list.len(), |
397 | block_transaction_count | 399 | block_transaction_count |
398 | ); | 400 | ); |
399 | let res_json = warp::reply::json(&GradeCoinResponse { | 401 | let res_json = warp::reply::json(&UserFeedback { |
400 | res: ResponseType::Error, | 402 | res: ResponseType::Error, |
401 | message: format!( | 403 | message: format!( |
402 | "There should be at least {} transactions in the block", | 404 | "There should be at least {} transactions in the block", |
@@ -420,7 +422,7 @@ pub async fn propose_block( | |||
420 | new_block.transaction_list[0] | 422 | new_block.transaction_list[0] |
421 | ); | 423 | ); |
422 | 424 | ||
423 | let res_json = warp::reply::json(&GradeCoinResponse { | 425 | let res_json = warp::reply::json(&UserFeedback { |
424 | res: ResponseType::Error, | 426 | res: ResponseType::Error, |
425 | message: "First transaction in the block is not found in the system".to_owned(), | 427 | message: "First transaction in the block is not found in the system".to_owned(), |
426 | }); | 428 | }); |
@@ -439,7 +441,7 @@ pub async fn propose_block( | |||
439 | new_block.transaction_list[0] | 441 | new_block.transaction_list[0] |
440 | ); | 442 | ); |
441 | 443 | ||
442 | let res_json = warp::reply::json(&GradeCoinResponse { | 444 | let res_json = warp::reply::json(&UserFeedback { |
443 | res: ResponseType::Error, | 445 | res: ResponseType::Error, |
444 | message: "User with that public key signature is not found in the database".to_owned(), | 446 | message: "User with that public key signature is not found in the database".to_owned(), |
445 | }); | 447 | }); |
@@ -455,7 +457,7 @@ pub async fn propose_block( | |||
455 | Err(below) => { | 457 | Err(below) => { |
456 | debug!("Something went wrong with the JWT {:?}", below); | 458 | debug!("Something went wrong with the JWT {:?}", below); |
457 | 459 | ||
458 | let res_json = warp::reply::json(&GradeCoinResponse { | 460 | let res_json = warp::reply::json(&UserFeedback { |
459 | res: ResponseType::Error, | 461 | res: ResponseType::Error, |
460 | message: below, | 462 | message: below, |
461 | }); | 463 | }); |
@@ -470,7 +472,7 @@ pub async fn propose_block( | |||
470 | "The Hash of the block {:?} did not match the hash given in jwt {:?}", | 472 | "The Hash of the block {:?} did not match the hash given in jwt {:?}", |
471 | new_block.hash, token_payload.claims.tha | 473 | new_block.hash, token_payload.claims.tha |
472 | ); | 474 | ); |
473 | let res_json = warp::reply::json(&GradeCoinResponse { | 475 | let res_json = warp::reply::json(&UserFeedback { |
474 | res: ResponseType::Error, | 476 | res: ResponseType::Error, |
475 | message: "The hash of the block did not match the hash given in JWT tha field" | 477 | message: "The hash of the block did not match the hash given in JWT tha field" |
476 | .to_owned(), | 478 | .to_owned(), |
@@ -481,7 +483,7 @@ pub async fn propose_block( | |||
481 | 483 | ||
482 | if !has_unique_elements(&new_block.transaction_list) { | 484 | if !has_unique_elements(&new_block.transaction_list) { |
483 | debug!("Block contains duplicate transactions!"); | 485 | debug!("Block contains duplicate transactions!"); |
484 | let res_json = warp::reply::json(&GradeCoinResponse { | 486 | let res_json = warp::reply::json(&UserFeedback { |
485 | res: ResponseType::Error, | 487 | res: ResponseType::Error, |
486 | message: "Block cannot contain duplicate transactions".to_owned(), | 488 | message: "Block cannot contain duplicate transactions".to_owned(), |
487 | }); | 489 | }); |
@@ -492,7 +494,7 @@ pub async fn propose_block( | |||
492 | // Are transactions in the block valid? | 494 | // Are transactions in the block valid? |
493 | for transaction_hash in &new_block.transaction_list { | 495 | for transaction_hash in &new_block.transaction_list { |
494 | if !pending_transactions.contains_key(transaction_hash) { | 496 | if !pending_transactions.contains_key(transaction_hash) { |
495 | let res_json = warp::reply::json(&GradeCoinResponse { | 497 | let res_json = warp::reply::json(&UserFeedback { |
496 | res: ResponseType::Error, | 498 | res: ResponseType::Error, |
497 | message: "Block contains an unknown transaction".to_owned(), | 499 | message: "Block contains an unknown transaction".to_owned(), |
498 | }); | 500 | }); |
@@ -516,7 +518,7 @@ pub async fn propose_block( | |||
516 | // Does the hash claimed in block match with the actual hash? | 518 | // Does the hash claimed in block match with the actual hash? |
517 | if hash_string != new_block.hash { | 519 | if hash_string != new_block.hash { |
518 | debug!("request was not telling the truth, hash values do not match"); | 520 | debug!("request was not telling the truth, hash values do not match"); |
519 | let res_json = warp::reply::json(&GradeCoinResponse { | 521 | let res_json = warp::reply::json(&UserFeedback { |
520 | res: ResponseType::Error, | 522 | res: ResponseType::Error, |
521 | message: "Given hash value does not match the actual block hash".to_owned(), | 523 | message: "Given hash value does not match the actual block hash".to_owned(), |
522 | }); | 524 | }); |
@@ -525,18 +527,22 @@ pub async fn propose_block( | |||
525 | } | 527 | } |
526 | 528 | ||
527 | // Are the n leftmost characters zero? | 529 | // Are the n leftmost characters zero? |
528 | let hash_correct = hash_string.chars() | 530 | let hash_correct = hash_string |
531 | .chars() | ||
529 | .take(db.config.hash_zeros.into()) | 532 | .take(db.config.hash_zeros.into()) |
530 | .all(|x| x == '0'); | 533 | .all(|x| x == '0'); |
531 | 534 | ||
532 | if !hash_correct { | 535 | if !hash_correct { |
533 | debug!("The hash does not have {} leftmost zero characters", db.config.hash_zeros); | 536 | debug!( |
534 | let res_json = warp::reply::json(&GradeCoinResponse { | 537 | "The hash does not have {} leftmost zero characters", |
538 | db.config.hash_zeros | ||
539 | ); | ||
540 | let res_json = warp::reply::json(&UserFeedback { | ||
535 | res: ResponseType::Error, | 541 | res: ResponseType::Error, |
536 | message: format!( | 542 | message: format!( |
537 | "Given block hash does not start with {} zero hexadecimal characters", | 543 | "Given block hash does not start with {} zero hexadecimal characters", |
538 | db.config.hash_zeros | 544 | db.config.hash_zeros |
539 | ), | 545 | ), |
540 | }); | 546 | }); |
541 | 547 | ||
542 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 548 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
@@ -553,10 +559,15 @@ pub async fn propose_block( | |||
553 | // Reward the block proposer | 559 | // Reward the block proposer |
554 | // All unwrap calls here are guaranteed to succeed because they are already checked above | 560 | // All unwrap calls here are guaranteed to succeed because they are already checked above |
555 | // See: internal_user_fingerprint, internal_user | 561 | // See: internal_user_fingerprint, internal_user |
556 | let coinbase = pending_transactions.get(&new_block.transaction_list[0]).unwrap(); | 562 | let coinbase = pending_transactions |
563 | .get(&new_block.transaction_list[0]) | ||
564 | .unwrap(); | ||
557 | let mut coinbase_user = users_store.get_mut(&coinbase.source).unwrap(); | 565 | let mut coinbase_user = users_store.get_mut(&coinbase.source).unwrap(); |
558 | coinbase_user.balance += db.config.block_reward; | 566 | coinbase_user.balance += db.config.block_reward; |
559 | debug!("{} block reward went to {:?} for mining the block", db.config.block_reward, coinbase_user); | 567 | debug!( |
568 | "{} block reward went to {} for mining the block", | ||
569 | db.config.block_reward, coinbase_user.user_id | ||
570 | ); | ||
560 | 571 | ||
561 | let mut holding: HashMap<String, Transaction> = HashMap::new(); | 572 | let mut holding: HashMap<String, Transaction> = HashMap::new(); |
562 | 573 | ||
@@ -615,7 +626,11 @@ pub async fn propose_block( | |||
615 | let block_json = serde_json::to_string(&new_block).unwrap(); | 626 | let block_json = serde_json::to_string(&new_block).unwrap(); |
616 | 627 | ||
617 | fs::write( | 628 | fs::write( |
618 | format!("blocks/{}/{}.block", db.config.name, new_block.timestamp.timestamp()), | 629 | format!( |
630 | "blocks/{}/{}.block", | ||
631 | db.config.name, | ||
632 | new_block.timestamp.timestamp() | ||
633 | ), | ||
619 | block_json, | 634 | block_json, |
620 | ) | 635 | ) |
621 | .unwrap(); | 636 | .unwrap(); |
@@ -626,7 +641,7 @@ pub async fn propose_block( | |||
626 | } | 641 | } |
627 | 642 | ||
628 | Ok(warp::reply::with_status( | 643 | Ok(warp::reply::with_status( |
629 | warp::reply::json(&GradeCoinResponse { | 644 | warp::reply::json(&UserFeedback { |
630 | res: ResponseType::Success, | 645 | res: ResponseType::Success, |
631 | message: "Block accepted, coinbase reward awarded".to_owned(), | 646 | message: "Block accepted, coinbase reward awarded".to_owned(), |
632 | }), | 647 | }), |
@@ -648,7 +663,10 @@ pub async fn propose_transaction( | |||
648 | token: String, | 663 | token: String, |
649 | db: Db, | 664 | db: Db, |
650 | ) -> Result<impl warp::Reply, warp::Rejection> { | 665 | ) -> Result<impl warp::Reply, warp::Rejection> { |
651 | warn!("[{}] New transaction proposal: {:?}", db.config.name, &new_transaction); | 666 | warn!( |
667 | "[{}] New transaction proposal: {:?}", | ||
668 | db.config.name, &new_transaction | ||
669 | ); | ||
652 | 670 | ||
653 | let users_store = db.users.read(); | 671 | let users_store = db.users.read(); |
654 | 672 | ||
@@ -662,7 +680,7 @@ pub async fn propose_transaction( | |||
662 | ); | 680 | ); |
663 | 681 | ||
664 | return Ok(warp::reply::with_status( | 682 | return Ok(warp::reply::with_status( |
665 | warp::reply::json(&GradeCoinResponse { | 683 | warp::reply::json(&UserFeedback { |
666 | res: ResponseType::Error, | 684 | res: ResponseType::Error, |
667 | message: "User with the given public key signature is not authorized".to_owned(), | 685 | message: "User with the given public key signature is not authorized".to_owned(), |
668 | }), | 686 | }), |
@@ -674,7 +692,7 @@ pub async fn propose_transaction( | |||
674 | debug!("Someone tried to send as the bot"); | 692 | debug!("Someone tried to send as the bot"); |
675 | 693 | ||
676 | return Ok(warp::reply::with_status( | 694 | return Ok(warp::reply::with_status( |
677 | warp::reply::json(&GradeCoinResponse { | 695 | warp::reply::json(&UserFeedback { |
678 | res: ResponseType::Error, | 696 | res: ResponseType::Error, |
679 | message: "Don's send transactions on behalf of bots".to_owned(), | 697 | message: "Don's send transactions on behalf of bots".to_owned(), |
680 | }), | 698 | }), |
@@ -693,7 +711,7 @@ pub async fn propose_transaction( | |||
693 | Err(below) => { | 711 | Err(below) => { |
694 | debug!("JWT Error: {:?}", below); | 712 | debug!("JWT Error: {:?}", below); |
695 | return Ok(warp::reply::with_status( | 713 | return Ok(warp::reply::with_status( |
696 | warp::reply::json(&GradeCoinResponse { | 714 | warp::reply::json(&UserFeedback { |
697 | res: ResponseType::Error, | 715 | res: ResponseType::Error, |
698 | message: below, | 716 | message: below, |
699 | }), | 717 | }), |
@@ -710,7 +728,7 @@ pub async fn propose_transaction( | |||
710 | ); | 728 | ); |
711 | 729 | ||
712 | return Ok(warp::reply::with_status( | 730 | return Ok(warp::reply::with_status( |
713 | warp::reply::json(&GradeCoinResponse { | 731 | warp::reply::json(&UserFeedback { |
714 | res: ResponseType::Error, | 732 | res: ResponseType::Error, |
715 | message: format!( | 733 | message: format!( |
716 | "Target of the transaction {} is not found in the system", | 734 | "Target of the transaction {} is not found in the system", |
@@ -723,8 +741,6 @@ pub async fn propose_transaction( | |||
723 | 741 | ||
724 | let transaction_id = calculate_transaction_id(&new_transaction.source, &new_transaction.target); | 742 | let transaction_id = calculate_transaction_id(&new_transaction.source, &new_transaction.target); |
725 | 743 | ||
726 | // OLD: Does this user have a pending transaction? | ||
727 | // NEW: Is this source:target pair unqiue? | ||
728 | { | 744 | { |
729 | let transactions = db.pending_transactions.read(); | 745 | let transactions = db.pending_transactions.read(); |
730 | debug!( | 746 | debug!( |
@@ -739,7 +755,7 @@ pub async fn propose_transaction( | |||
739 | ); | 755 | ); |
740 | 756 | ||
741 | return Ok(warp::reply::with_status( | 757 | return Ok(warp::reply::with_status( |
742 | warp::reply::json(&GradeCoinResponse { | 758 | warp::reply::json(&UserFeedback { |
743 | res: ResponseType::Error, | 759 | res: ResponseType::Error, |
744 | message: "This user already has another pending transaction".to_owned(), | 760 | message: "This user already has another pending transaction".to_owned(), |
745 | }), | 761 | }), |
@@ -752,7 +768,7 @@ pub async fn propose_transaction( | |||
752 | debug!("transaction source and target are the same",); | 768 | debug!("transaction source and target are the same",); |
753 | 769 | ||
754 | return Ok(warp::reply::with_status( | 770 | return Ok(warp::reply::with_status( |
755 | warp::reply::json(&GradeCoinResponse { | 771 | warp::reply::json(&UserFeedback { |
756 | res: ResponseType::Error, | 772 | res: ResponseType::Error, |
757 | message: "transaction to yourself, you had to try didn't you? :)".to_owned(), | 773 | message: "transaction to yourself, you had to try didn't you? :)".to_owned(), |
758 | }), | 774 | }), |
@@ -769,7 +785,7 @@ pub async fn propose_transaction( | |||
769 | tx_lower_limit, tx_upper_limit, new_transaction.amount | 785 | tx_lower_limit, tx_upper_limit, new_transaction.amount |
770 | ); | 786 | ); |
771 | return Ok(warp::reply::with_status( | 787 | return Ok(warp::reply::with_status( |
772 | warp::reply::json(&GradeCoinResponse { | 788 | warp::reply::json(&UserFeedback { |
773 | res: ResponseType::Error, | 789 | res: ResponseType::Error, |
774 | message: format!( | 790 | message: format!( |
775 | "Transaction amount should be between {} and {}", | 791 | "Transaction amount should be between {} and {}", |
@@ -787,7 +803,7 @@ pub async fn propose_transaction( | |||
787 | internal_user.balance, new_transaction.amount | 803 | internal_user.balance, new_transaction.amount |
788 | ); | 804 | ); |
789 | return Ok(warp::reply::with_status( | 805 | return Ok(warp::reply::with_status( |
790 | warp::reply::json(&GradeCoinResponse { | 806 | warp::reply::json(&UserFeedback { |
791 | res: ResponseType::Error, | 807 | res: ResponseType::Error, |
792 | message: "User does not have enough balance in their account for this transaction" | 808 | message: "User does not have enough balance in their account for this transaction" |
793 | .to_owned(), | 809 | .to_owned(), |
@@ -806,7 +822,7 @@ pub async fn propose_transaction( | |||
806 | let hashed_transaction = Md5::digest(serd_tx.as_bytes()); | 822 | let hashed_transaction = Md5::digest(serd_tx.as_bytes()); |
807 | if token_payload.claims.tha != format!("{:x}", hashed_transaction) { | 823 | if token_payload.claims.tha != format!("{:x}", hashed_transaction) { |
808 | return Ok(warp::reply::with_status( | 824 | return Ok(warp::reply::with_status( |
809 | warp::reply::json(&GradeCoinResponse { | 825 | warp::reply::json(&UserFeedback { |
810 | res: ResponseType::Error, | 826 | res: ResponseType::Error, |
811 | message: "The hash of the transaction did not match the hash given in JWT" | 827 | message: "The hash of the transaction did not match the hash given in JWT" |
812 | .to_owned(), | 828 | .to_owned(), |
@@ -815,14 +831,17 @@ pub async fn propose_transaction( | |||
815 | )); | 831 | )); |
816 | } | 832 | } |
817 | 833 | ||
818 | warn!("[{}] ACCEPTED TRANSACTION {:?}", db.config.name, new_transaction); | 834 | warn!( |
835 | "[{}] ACCEPTED TRANSACTION {:?}", | ||
836 | db.config.name, new_transaction | ||
837 | ); | ||
819 | 838 | ||
820 | let mut transactions = db.pending_transactions.write(); | 839 | let mut transactions = db.pending_transactions.write(); |
821 | 840 | ||
822 | transactions.insert(transaction_id, new_transaction); | 841 | transactions.insert(transaction_id, new_transaction); |
823 | 842 | ||
824 | Ok(warp::reply::with_status( | 843 | Ok(warp::reply::with_status( |
825 | warp::reply::json(&GradeCoinResponse { | 844 | warp::reply::json(&UserFeedback { |
826 | res: ResponseType::Success, | 845 | res: ResponseType::Success, |
827 | message: "Transaction accepted".to_owned(), | 846 | message: "Transaction accepted".to_owned(), |
828 | }), | 847 | }), |
@@ -835,8 +854,6 @@ pub async fn propose_transaction( | |||
835 | /// Cannot fail | 854 | /// Cannot fail |
836 | /// Mostly around for debug purposes | 855 | /// Mostly around for debug purposes |
837 | pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { | 856 | pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { |
838 | debug!("GET /block, list_blocks() is handling"); | ||
839 | |||
840 | let block = db.blockchain.read(); | 857 | let block = db.blockchain.read(); |
841 | 858 | ||
842 | Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) | 859 | Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) |
diff --git a/src/main.rs b/src/main.rs index 595ce3b..93397fa 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -46,21 +46,21 @@ | |||
46 | #![warn(clippy::all, clippy::pedantic)] | 46 | #![warn(clippy::all, clippy::pedantic)] |
47 | #![allow(clippy::unused_async)] | 47 | #![allow(clippy::unused_async)] |
48 | 48 | ||
49 | mod block; | ||
50 | mod config; | ||
49 | mod custom_filters; | 51 | mod custom_filters; |
50 | mod db; | 52 | mod db; |
51 | mod handlers; | 53 | mod handlers; |
52 | mod routes; | 54 | mod routes; |
53 | mod block; | ||
54 | mod student; | 55 | mod student; |
55 | mod config; | ||
56 | 56 | ||
57 | use crate::config::Config; | ||
57 | pub use block::{Fingerprint, Id}; | 58 | pub use block::{Fingerprint, Id}; |
58 | use db::Db; | 59 | use db::Db; |
59 | use lazy_static::lazy_static; | 60 | use lazy_static::lazy_static; |
61 | use log::error; | ||
60 | use std::fs; | 62 | use std::fs; |
61 | use crate::config::Config; | 63 | use warp::Filter; |
62 | use warp::{Filter}; | ||
63 | use log::{error}; | ||
64 | 64 | ||
65 | #[tokio::main] | 65 | #[tokio::main] |
66 | async fn main() { | 66 | async fn main() { |
@@ -73,13 +73,11 @@ async fn main() { | |||
73 | args.push("config.yaml".to_string()); | 73 | args.push("config.yaml".to_string()); |
74 | } | 74 | } |
75 | 75 | ||
76 | let combined_routes = args.into_iter() | 76 | let combined_routes = args |
77 | .into_iter() | ||
77 | .skip(1) // Skip the program name | 78 | .skip(1) // Skip the program name |
78 | .filter_map(|filename| { | 79 | .filter_map(|filename| { |
79 | match Config::read(&filename) { | 80 | Config::read(&filename).map(|config| routes::network(Db::new(config))) |
80 | Some(config) => Some(routes::network(Db::new(config))), | ||
81 | None => None, | ||
82 | } | ||
83 | }) | 81 | }) |
84 | .reduce(|routes, route| routes.or(route).unify().boxed()); | 82 | .reduce(|routes, route| routes.or(route).unify().boxed()); |
85 | 83 | ||
@@ -89,7 +87,7 @@ async fn main() { | |||
89 | // Exit the program if there's no successfully loaded config file. | 87 | // Exit the program if there's no successfully loaded config file. |
90 | error!("Failed to load any config files!"); | 88 | error!("Failed to load any config files!"); |
91 | return; | 89 | return; |
92 | }, | 90 | } |
93 | }; | 91 | }; |
94 | 92 | ||
95 | // gradecoin-site (zola) outputs a public/, we serve it here | 93 | // gradecoin-site (zola) outputs a public/, we serve it here |
diff --git a/src/routes.rs b/src/routes.rs index 651626a..0df3470 100644 --- a/src/routes.rs +++ b/src/routes.rs | |||
@@ -3,13 +3,16 @@ | |||
3 | use crate::custom_filters; | 3 | use crate::custom_filters; |
4 | use crate::handlers; | 4 | use crate::handlers; |
5 | use crate::Db; | 5 | use crate::Db; |
6 | use warp::{Filter, filters::BoxedFilter, Rejection, Reply}; | 6 | use log::info; |
7 | use log::{info}; | 7 | use warp::{filters::BoxedFilter, Filter, Rejection, Reply}; |
8 | 8 | ||
9 | /// Every route combined for a single network | 9 | /// Every route combined for a single network |
10 | pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { | 10 | pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { |
11 | let url_prefix = db.config.url_prefix.clone(); | 11 | let url_prefix = db.config.url_prefix.clone(); |
12 | info!("{} will be served at endpoint /{}", db.config.name, url_prefix); | 12 | info!( |
13 | "{} will be served at endpoint /{}", | ||
14 | db.config.name, url_prefix | ||
15 | ); | ||
13 | let root = if url_prefix.is_empty() { | 16 | let root = if url_prefix.is_empty() { |
14 | // warp::path does not like empty url_prefix | 17 | // warp::path does not like empty url_prefix |
15 | // We need to handle this case separately | 18 | // We need to handle this case separately |
@@ -19,14 +22,14 @@ pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { | |||
19 | }; | 22 | }; |
20 | root.and( | 23 | root.and( |
21 | transaction_list(db.clone()) | 24 | transaction_list(db.clone()) |
22 | .or(get_config_route(db.clone())) | 25 | .or(get_config_route(db.clone())) |
23 | .or(register_user(db.clone())) | 26 | .or(register_user(db.clone())) |
24 | .or(auth_transaction_propose(db.clone())) | 27 | .or(auth_transaction_propose(db.clone())) |
25 | .or(auth_block_propose(db.clone())) | 28 | .or(auth_block_propose(db.clone())) |
26 | .or(list_users(db.clone())) | 29 | .or(list_users(db.clone())) |
27 | .or(block_list(db)) | 30 | .or(block_list(db)), |
28 | ) | 31 | ) |
29 | .boxed() | 32 | .boxed() |
30 | } | 33 | } |
31 | 34 | ||
32 | /// GET /config warp route | 35 | /// GET /config warp route |
diff --git a/src/student.rs b/src/student.rs index b6d99cd..5af512e 100644 --- a/src/student.rs +++ b/src/student.rs | |||
@@ -26,7 +26,13 @@ pub struct User { | |||
26 | 26 | ||
27 | impl fmt::Display for User { | 27 | impl fmt::Display for User { |
28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
29 | write!(f, "{}", self.user_id) | 29 | write!(f, "{}", self.user_id.get_id()) |
30 | } | ||
31 | } | ||
32 | |||
33 | impl fmt::Display for UserAtRest { | ||
34 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
35 | write!(f, "{}", self.user) | ||
30 | } | 36 | } |
31 | } | 37 | } |
32 | 38 | ||