From 21095d29f348d8c965df9871bbc0644de5000a53 Mon Sep 17 00:00:00 2001 From: Yigit Sever Date: Sat, 23 Apr 2022 20:29:36 +0300 Subject: Format, refactor, succinct errors --- src/block.rs | 1 - src/config.rs | 10 ++--- src/db.rs | 18 ++++----- src/handlers.rs | 119 ++++++++++++++++++++++++++++++++------------------------ src/main.rs | 20 +++++----- src/routes.rs | 25 ++++++------ 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 @@ //! Users are held in memory and they're also backed up to text files use chrono::{NaiveDate, NaiveDateTime}; use serde::{Deserialize, Serialize}; -use std::{string::String, vec::Vec}; pub type Fingerprint = String; 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 @@ //! # Configuration //! //! This module holds the data structures for network configuration. -use serde::{Deserialize, Serialize}; use log::{error, info}; +use serde::{Deserialize, Serialize}; /// Configuration for a single network #[derive(Debug, Serialize, Deserialize, Clone, Default)] @@ -29,7 +29,7 @@ pub struct Config { /// Valid blocks should have this many transactions pub block_transaction_count: u8, - + /// How many zero hexadecimal characters should a correct hash start with? pub hash_zeros: u8, @@ -58,15 +58,15 @@ impl Config { error!("Cannot read config file: {}", filename); error!("Error: {:?}", e); return None; - }, + } }; - let config : Config = match serde_yaml::from_reader(file) { + let config: Config = match serde_yaml::from_reader(file) { Ok(c) => c, Err(e) => { error!("Cannot parse config file: {}", filename); error!("Error: {:?}", e); return None; - }, + } }; // File closes automatically when it goes out of scope. 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 @@ //! [`Db::users`] is the in memory representation of the users, //! with their public keys, `metu_ids` and gradecoin balances. use crate::block::{Block, Fingerprint, Id, Transaction}; -use crate::student::{MetuId, User, UserAtRest}; use crate::config::Config; -use log::debug; +use crate::student::{MetuId, User, UserAtRest}; +use log::info; use parking_lot::RwLock; use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc}; @@ -32,7 +32,7 @@ impl Db { // Load bots let users: HashMap = get_friendly_users(); - // Read the list of users who can register + // Load the list of users who can register let preapproved_users = read_approved_users(&config.preapproved_users); let mut db = Db { @@ -43,21 +43,21 @@ impl Db { preapproved_users, }; - // Read blocks + // Load the latest block, continue from where we left off if let Some(block_path) = last_block_content(&db.config.name) { db.populate_with_last_block(block_path); } - // Read users + // Load the users that had registered themselves if let Ok(users_path) = read_users(&db.config.name) { db.populate_with_users(users_path); } - return db; + db } fn populate_with_last_block(&mut self, path: String) { - debug!("Populating db with the latest block {}", path); + info!("Populating db with the latest block {}", path); let file = fs::read(path).unwrap(); let json = std::str::from_utf8(&file).unwrap(); let block: Block = serde_json::from_str(json).unwrap(); @@ -71,7 +71,7 @@ impl Db { String::from_utf8(file_content).expect("we have written a malformed user file"); let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap(); - debug!("Populating db with user: {:?}", user_at_rest); + info!("Populating db with user: {:?}", user_at_rest); self.users .write() .insert(user_at_rest.fingerprint, user_at_rest.user); @@ -122,7 +122,7 @@ fn read_block_name(config_name: &str) -> io::Result> { } fn parse_block(path: &str) -> u64 { - let start_pos = path.rfind("/").unwrap() + 1; + let start_pos = path.rfind('/').unwrap() + 1; let end_pos = path.find(".block").unwrap(); let block_str = path[start_pos..end_pos].to_string(); 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; type Aes128Cbc = Cbc; #[derive(Serialize, Debug)] -struct GradeCoinResponse { +struct UserFeedback { res: ResponseType, message: String, } @@ -97,7 +97,7 @@ pub async fn authenticate_user( request: InitialAuthRequest, db: Db, ) -> Result { - debug!("POST /register, authenticate_user() is handling"); + debug!("[{}] New user registration attempt", db.config.name); // In essence PEM files are just base64 encoded versions of the DER encoded data. // ~tls.mbed.org @@ -113,7 +113,7 @@ pub async fn authenticate_user( &request.key, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "\"key\" field of initial auth request was not base64 encoded: {}, {}", @@ -134,7 +134,7 @@ pub async fn authenticate_user( err, &key_ciphertext ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Failed to decrypt the 'key_ciphertext' field of the auth request" .to_owned(), @@ -153,7 +153,7 @@ pub async fn authenticate_user( &request.iv, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "\"iv\" field of initial auth request was not base64 encoded: {}, {}", @@ -174,11 +174,11 @@ pub async fn authenticate_user( &temp_key, &request.iv, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( - "Could not create a cipher from given 'temp_key': {:?} and 'IV': {}", - &temp_key, &request.iv + "Could not create a cipher from given 'temp_key': {:?} and 'IV': {}, {}", + &temp_key, &request.iv, err ), }); @@ -195,7 +195,7 @@ pub async fn authenticate_user( &request.c, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "\"c\" field of initial auth request was not base64 encoded: {}, {}", @@ -218,7 +218,7 @@ pub async fn authenticate_user( &buf, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Failed to decrypt the 'c' field of the auth request, 'iv' and 'k_temp' were valid so far though" .to_owned(), @@ -237,7 +237,7 @@ pub async fn authenticate_user( &auth_plaintext, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "P_AR couldn't get converted to UTF-8, please check your encoding" .to_owned(), @@ -256,7 +256,7 @@ pub async fn authenticate_user( &utf8_auth_plaintext, err ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "The P_AR JSON did not serialize correctly, did it include all 3 fields 'student_id', 'passwd' and 'public_key'?".to_owned(), }); @@ -273,7 +273,7 @@ pub async fn authenticate_user( "Someone tried to auth with invalid credentials: {} {}", &request.student_id, &request.passwd ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "The credentials given ('student_id', 'passwd') cannot hold a Gradecoin account" @@ -289,7 +289,8 @@ pub async fn authenticate_user( for (_, user) in userlist.iter() { if user.user_id == privileged_student_id { - let res_json = warp::reply::json(&GradeCoinResponse { + debug!("{} attempted to authenticate again", user.user_id); + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "This user is already authenticated, do you think this is a mistake? Contact me" @@ -302,7 +303,7 @@ pub async fn authenticate_user( // We're using this as the validator instead of anything reasonable if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() { - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "The RSA 'public_key' in 'P_AR' is not in valid PEM format".to_owned(), }); @@ -319,7 +320,7 @@ pub async fn authenticate_user( is_bot: false, }; - debug!("NEW USER: {:?}", &new_user); + warn!("A new user has authenticated: {}", &new_user.user_id); // save the user to disk let user_at_rest_json = serde_json::to_string(&UserAtRest { @@ -335,13 +336,14 @@ pub async fn authenticate_user( fs::write( format!("users/{}/{}.guy", db.config.name, new_user.user_id), - user_at_rest_json - ).unwrap(); + user_at_rest_json, + ) + .unwrap(); let mut userlist = db.users.write(); userlist.insert(fingerprint.clone(), new_user); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Success, message: format!( "You have authenticated to use Gradecoin with identifier {}", @@ -396,7 +398,7 @@ pub async fn propose_block( new_block.transaction_list.len(), block_transaction_count ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "There should be at least {} transactions in the block", @@ -420,7 +422,7 @@ pub async fn propose_block( new_block.transaction_list[0] ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "First transaction in the block is not found in the system".to_owned(), }); @@ -439,7 +441,7 @@ pub async fn propose_block( new_block.transaction_list[0] ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "User with that public key signature is not found in the database".to_owned(), }); @@ -455,7 +457,7 @@ pub async fn propose_block( Err(below) => { debug!("Something went wrong with the JWT {:?}", below); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: below, }); @@ -470,7 +472,7 @@ pub async fn propose_block( "The Hash of the block {:?} did not match the hash given in jwt {:?}", new_block.hash, token_payload.claims.tha ); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "The hash of the block did not match the hash given in JWT tha field" .to_owned(), @@ -481,7 +483,7 @@ pub async fn propose_block( if !has_unique_elements(&new_block.transaction_list) { debug!("Block contains duplicate transactions!"); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Block cannot contain duplicate transactions".to_owned(), }); @@ -492,7 +494,7 @@ pub async fn propose_block( // Are transactions in the block valid? for transaction_hash in &new_block.transaction_list { if !pending_transactions.contains_key(transaction_hash) { - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Block contains an unknown transaction".to_owned(), }); @@ -516,7 +518,7 @@ pub async fn propose_block( // Does the hash claimed in block match with the actual hash? if hash_string != new_block.hash { debug!("request was not telling the truth, hash values do not match"); - let res_json = warp::reply::json(&GradeCoinResponse { + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Given hash value does not match the actual block hash".to_owned(), }); @@ -525,18 +527,22 @@ pub async fn propose_block( } // Are the n leftmost characters zero? - let hash_correct = hash_string.chars() + let hash_correct = hash_string + .chars() .take(db.config.hash_zeros.into()) .all(|x| x == '0'); if !hash_correct { - debug!("The hash does not have {} leftmost zero characters", db.config.hash_zeros); - let res_json = warp::reply::json(&GradeCoinResponse { + debug!( + "The hash does not have {} leftmost zero characters", + db.config.hash_zeros + ); + let res_json = warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "Given block hash does not start with {} zero hexadecimal characters", db.config.hash_zeros - ), + ), }); return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); @@ -553,10 +559,15 @@ pub async fn propose_block( // Reward the block proposer // All unwrap calls here are guaranteed to succeed because they are already checked above // See: internal_user_fingerprint, internal_user - let coinbase = pending_transactions.get(&new_block.transaction_list[0]).unwrap(); + let coinbase = pending_transactions + .get(&new_block.transaction_list[0]) + .unwrap(); let mut coinbase_user = users_store.get_mut(&coinbase.source).unwrap(); coinbase_user.balance += db.config.block_reward; - debug!("{} block reward went to {:?} for mining the block", db.config.block_reward, coinbase_user); + debug!( + "{} block reward went to {} for mining the block", + db.config.block_reward, coinbase_user.user_id + ); let mut holding: HashMap = HashMap::new(); @@ -615,7 +626,11 @@ pub async fn propose_block( let block_json = serde_json::to_string(&new_block).unwrap(); fs::write( - format!("blocks/{}/{}.block", db.config.name, new_block.timestamp.timestamp()), + format!( + "blocks/{}/{}.block", + db.config.name, + new_block.timestamp.timestamp() + ), block_json, ) .unwrap(); @@ -626,7 +641,7 @@ pub async fn propose_block( } Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Success, message: "Block accepted, coinbase reward awarded".to_owned(), }), @@ -648,7 +663,10 @@ pub async fn propose_transaction( token: String, db: Db, ) -> Result { - warn!("[{}] New transaction proposal: {:?}", db.config.name, &new_transaction); + warn!( + "[{}] New transaction proposal: {:?}", + db.config.name, &new_transaction + ); let users_store = db.users.read(); @@ -662,7 +680,7 @@ pub async fn propose_transaction( ); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "User with the given public key signature is not authorized".to_owned(), }), @@ -674,7 +692,7 @@ pub async fn propose_transaction( debug!("Someone tried to send as the bot"); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "Don's send transactions on behalf of bots".to_owned(), }), @@ -693,7 +711,7 @@ pub async fn propose_transaction( Err(below) => { debug!("JWT Error: {:?}", below); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: below, }), @@ -710,7 +728,7 @@ pub async fn propose_transaction( ); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "Target of the transaction {} is not found in the system", @@ -723,8 +741,6 @@ pub async fn propose_transaction( let transaction_id = calculate_transaction_id(&new_transaction.source, &new_transaction.target); - // OLD: Does this user have a pending transaction? - // NEW: Is this source:target pair unqiue? { let transactions = db.pending_transactions.read(); debug!( @@ -739,7 +755,7 @@ pub async fn propose_transaction( ); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "This user already has another pending transaction".to_owned(), }), @@ -752,7 +768,7 @@ pub async fn propose_transaction( debug!("transaction source and target are the same",); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "transaction to yourself, you had to try didn't you? :)".to_owned(), }), @@ -769,7 +785,7 @@ pub async fn propose_transaction( tx_lower_limit, tx_upper_limit, new_transaction.amount ); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: format!( "Transaction amount should be between {} and {}", @@ -787,7 +803,7 @@ pub async fn propose_transaction( internal_user.balance, new_transaction.amount ); return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "User does not have enough balance in their account for this transaction" .to_owned(), @@ -806,7 +822,7 @@ pub async fn propose_transaction( let hashed_transaction = Md5::digest(serd_tx.as_bytes()); if token_payload.claims.tha != format!("{:x}", hashed_transaction) { return Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Error, message: "The hash of the transaction did not match the hash given in JWT" .to_owned(), @@ -815,14 +831,17 @@ pub async fn propose_transaction( )); } - warn!("[{}] ACCEPTED TRANSACTION {:?}", db.config.name, new_transaction); + warn!( + "[{}] ACCEPTED TRANSACTION {:?}", + db.config.name, new_transaction + ); let mut transactions = db.pending_transactions.write(); transactions.insert(transaction_id, new_transaction); Ok(warp::reply::with_status( - warp::reply::json(&GradeCoinResponse { + warp::reply::json(&UserFeedback { res: ResponseType::Success, message: "Transaction accepted".to_owned(), }), @@ -835,8 +854,6 @@ pub async fn propose_transaction( /// Cannot fail /// Mostly around for debug purposes pub async fn list_blocks(db: Db) -> Result { - debug!("GET /block, list_blocks() is handling"); - let block = db.blockchain.read(); 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 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::unused_async)] +mod block; +mod config; mod custom_filters; mod db; mod handlers; mod routes; -mod block; mod student; -mod config; +use crate::config::Config; pub use block::{Fingerprint, Id}; use db::Db; use lazy_static::lazy_static; +use log::error; use std::fs; -use crate::config::Config; -use warp::{Filter}; -use log::{error}; +use warp::Filter; #[tokio::main] async fn main() { @@ -73,13 +73,11 @@ async fn main() { args.push("config.yaml".to_string()); } - let combined_routes = args.into_iter() + let combined_routes = args + .into_iter() .skip(1) // Skip the program name .filter_map(|filename| { - match Config::read(&filename) { - Some(config) => Some(routes::network(Db::new(config))), - None => None, - } + Config::read(&filename).map(|config| routes::network(Db::new(config))) }) .reduce(|routes, route| routes.or(route).unify().boxed()); @@ -89,7 +87,7 @@ async fn main() { // Exit the program if there's no successfully loaded config file. error!("Failed to load any config files!"); return; - }, + } }; // 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 @@ use crate::custom_filters; use crate::handlers; use crate::Db; -use warp::{Filter, filters::BoxedFilter, Rejection, Reply}; -use log::{info}; +use log::info; +use warp::{filters::BoxedFilter, Filter, Rejection, Reply}; /// Every route combined for a single network pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { let url_prefix = db.config.url_prefix.clone(); - info!("{} will be served at endpoint /{}", db.config.name, url_prefix); + info!( + "{} will be served at endpoint /{}", + db.config.name, url_prefix + ); let root = if url_prefix.is_empty() { // warp::path does not like empty url_prefix // We need to handle this case separately @@ -19,14 +22,14 @@ pub fn network(db: Db) -> BoxedFilter<(impl Reply,)> { }; root.and( transaction_list(db.clone()) - .or(get_config_route(db.clone())) - .or(register_user(db.clone())) - .or(auth_transaction_propose(db.clone())) - .or(auth_block_propose(db.clone())) - .or(list_users(db.clone())) - .or(block_list(db)) - ) - .boxed() + .or(get_config_route(db.clone())) + .or(register_user(db.clone())) + .or(auth_transaction_propose(db.clone())) + .or(auth_block_propose(db.clone())) + .or(list_users(db.clone())) + .or(block_list(db)), + ) + .boxed() } /// 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 { impl fmt::Display for User { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.user_id) + write!(f, "{}", self.user_id.get_id()) + } +} + +impl fmt::Display for UserAtRest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.user) } } -- cgit v1.2.3-70-g09d2