aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/block.rs1
-rw-r--r--src/config.rs10
-rw-r--r--src/db.rs18
-rw-r--r--src/handlers.rs119
-rw-r--r--src/main.rs20
-rw-r--r--src/routes.rs25
-rw-r--r--src/student.rs8
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
10use chrono::{NaiveDate, NaiveDateTime}; 10use chrono::{NaiveDate, NaiveDateTime};
11use serde::{Deserialize, Serialize}; 11use serde::{Deserialize, Serialize};
12use std::{string::String, vec::Vec};
13 12
14pub type Fingerprint = String; 13pub type Fingerprint = String;
15pub type Id = String; 14pub 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.
4use serde::{Deserialize, Serialize};
5use log::{error, info}; 4use log::{error, info};
5use 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);
diff --git a/src/db.rs b/src/db.rs
index 70204a2..64be0c1 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -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.
11use crate::block::{Block, Fingerprint, Id, Transaction}; 11use crate::block::{Block, Fingerprint, Id, Transaction};
12use crate::student::{MetuId, User, UserAtRest};
13use crate::config::Config; 12use crate::config::Config;
14use log::debug; 13use crate::student::{MetuId, User, UserAtRest};
14use log::info;
15use parking_lot::RwLock; 15use parking_lot::RwLock;
16use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc}; 16use 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
124fn parse_block(path: &str) -> u64 { 124fn 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;
30type Aes128Cbc = Cbc<Aes128, Pkcs7>; 30type Aes128Cbc = Cbc<Aes128, Pkcs7>;
31 31
32#[derive(Serialize, Debug)] 32#[derive(Serialize, Debug)]
33struct GradeCoinResponse { 33struct 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
837pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { 856pub 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
49mod block;
50mod config;
49mod custom_filters; 51mod custom_filters;
50mod db; 52mod db;
51mod handlers; 53mod handlers;
52mod routes; 54mod routes;
53mod block;
54mod student; 55mod student;
55mod config;
56 56
57use crate::config::Config;
57pub use block::{Fingerprint, Id}; 58pub use block::{Fingerprint, Id};
58use db::Db; 59use db::Db;
59use lazy_static::lazy_static; 60use lazy_static::lazy_static;
61use log::error;
60use std::fs; 62use std::fs;
61use crate::config::Config; 63use warp::Filter;
62use warp::{Filter};
63use log::{error};
64 64
65#[tokio::main] 65#[tokio::main]
66async fn main() { 66async 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 @@
3use crate::custom_filters; 3use crate::custom_filters;
4use crate::handlers; 4use crate::handlers;
5use crate::Db; 5use crate::Db;
6use warp::{Filter, filters::BoxedFilter, Rejection, Reply}; 6use log::info;
7use log::{info}; 7use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
8 8
9/// Every route combined for a single network 9/// Every route combined for a single network
10pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { 10pub 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
27impl fmt::Display for User { 27impl 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
33impl 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