diff options
| author | Yigit Sever | 2021-04-25 22:23:58 +0300 |
|---|---|---|
| committer | Yigit Sever | 2021-04-25 22:23:58 +0300 |
| commit | 446cd6bdd6234ffba76a0e440c606686053f0ba0 (patch) | |
| tree | 5753559b01e1d51e64b5cce8da31f9ca106ef3d5 | |
| parent | 63d08a9f120e842dcc5a34a1db6b39957c643b30 (diff) | |
| download | gradecoin-446cd6bdd6234ffba76a0e440c606686053f0ba0.tar.gz gradecoin-446cd6bdd6234ffba76a0e440c606686053f0ba0.tar.bz2 gradecoin-446cd6bdd6234ffba76a0e440c606686053f0ba0.zip | |
Went over error messages
| -rw-r--r-- | src/handlers.rs | 188 |
1 files changed, 127 insertions, 61 deletions
diff --git a/src/handlers.rs b/src/handlers.rs index 123f70e..7204aa7 100644 --- a/src/handlers.rs +++ b/src/handlers.rs | |||
| @@ -16,13 +16,21 @@ use sha2::Sha256; | |||
| 16 | use std::collections::{HashMap, HashSet}; | 16 | use std::collections::{HashMap, HashSet}; |
| 17 | use std::convert::Infallible; | 17 | use std::convert::Infallible; |
| 18 | use std::fs; | 18 | use std::fs; |
| 19 | use std::hash::Hash; | ||
| 19 | use warp::{http::StatusCode, reply}; | 20 | use warp::{http::StatusCode, reply}; |
| 20 | 21 | ||
| 21 | use crate::PRIVATE_KEY; | 22 | use crate::PRIVATE_KEY; |
| 22 | const BLOCK_TRANSACTION_COUNT: u8 = 1; | 23 | |
| 24 | // Valid blocks should have this many transactions | ||
| 25 | const BLOCK_TRANSACTION_COUNT: u8 = 5; | ||
| 26 | // Inital registration bonus | ||
| 23 | const REGISTER_BONUS: u16 = 40; | 27 | const REGISTER_BONUS: u16 = 40; |
| 28 | // Coinbase reward | ||
| 24 | const BLOCK_REWARD: u16 = 3; | 29 | const BLOCK_REWARD: u16 = 3; |
| 30 | // Transaction amount limit | ||
| 25 | const TX_UPPER_LIMIT: u16 = 2; | 31 | const TX_UPPER_LIMIT: u16 = 2; |
| 32 | // Transaction traffic reward | ||
| 33 | const TX_TRAFFIC_REWARD: u16 = 1; | ||
| 26 | 34 | ||
| 27 | // Encryption primitive | 35 | // Encryption primitive |
| 28 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; | 36 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; |
| @@ -108,42 +116,68 @@ pub async fn authenticate_user( | |||
| 108 | 116 | ||
| 109 | let padding = PaddingScheme::new_oaep::<sha2::Sha256>(); | 117 | let padding = PaddingScheme::new_oaep::<sha2::Sha256>(); |
| 110 | 118 | ||
| 119 | // Peel away the base64 layer from "key" field | ||
| 111 | let key_ciphertext = match base64::decode(&request.key) { | 120 | let key_ciphertext = match base64::decode(&request.key) { |
| 112 | Ok(c) => c, | 121 | Ok(c) => c, |
| 113 | Err(err) => { | 122 | Err(err) => { |
| 114 | debug!( | 123 | debug!( |
| 115 | "The ciphertext of the key was not base64 encoded {}, {}", | 124 | "\"key\" field of initial auth request was not base64 encoded: {}, {}", |
| 116 | &request.key, err | 125 | &request.key, err |
| 117 | ); | 126 | ); |
| 118 | 127 | ||
| 119 | let res_json = warp::reply::json(&GradeCoinResponse { | 128 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 120 | res: ResponseType::Error, | 129 | res: ResponseType::Error, |
| 121 | message: "The ciphertext of the key was not base64 encoded {}, {}".to_owned(), | 130 | message: format!( |
| 131 | "\"key\" field of initial auth request was not base64 encoded: {}, {}", | ||
| 132 | &request.key, err | ||
| 133 | ), | ||
| 122 | }); | 134 | }); |
| 123 | 135 | ||
| 124 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 136 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 125 | } | 137 | } |
| 126 | }; | 138 | }; |
| 127 | 139 | ||
| 140 | // Decrypt the "key" field using Gradecoin's private key | ||
| 128 | let temp_key = match gradecoin_private_key.decrypt(padding, &key_ciphertext) { | 141 | let temp_key = match gradecoin_private_key.decrypt(padding, &key_ciphertext) { |
| 129 | Ok(k) => k, | 142 | Ok(k) => k, |
| 130 | Err(err) => { | 143 | Err(err) => { |
| 131 | debug!( | 144 | debug!( |
| 132 | "Failed to decrypt ciphertext {:?}, {}", | 145 | "Failed to decrypt ciphertext of the key with Gradecoin's public key: {}. Key was {:?}", |
| 133 | &key_ciphertext, err | 146 | err, &key_ciphertext |
| 134 | ); | 147 | ); |
| 135 | 148 | ||
| 136 | let res_json = warp::reply::json(&GradeCoinResponse { | 149 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 137 | res: ResponseType::Error, | 150 | res: ResponseType::Error, |
| 138 | message: "Failed to decrypt the ciphertext of the temporary key".to_owned(), | 151 | message: "Failed to decrypt the 'key_ciphertext' field of the auth request" |
| 152 | .to_owned(), | ||
| 139 | }); | 153 | }); |
| 140 | 154 | ||
| 141 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 155 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 142 | } | 156 | } |
| 143 | }; | 157 | }; |
| 144 | 158 | ||
| 145 | let byte_iv = base64::decode(&request.iv).unwrap(); | 159 | // Peel away the base64 from the iv field as well |
| 160 | let byte_iv = match base64::decode(&request.iv) { | ||
| 161 | Ok(iv) => iv, | ||
| 162 | Err(err) => { | ||
| 163 | debug!( | ||
| 164 | "\"iv\" field of initial auth request was not base64 encoded: {}, {}", | ||
| 165 | &request.iv, err | ||
| 166 | ); | ||
| 146 | 167 | ||
| 168 | let res_json = warp::reply::json(&GradeCoinResponse { | ||
| 169 | res: ResponseType::Error, | ||
| 170 | message: format!( | ||
| 171 | "\"iv\" field of initial auth request was not base64 encoded: {}, {}", | ||
| 172 | &request.iv, err | ||
| 173 | ), | ||
| 174 | }); | ||
| 175 | |||
| 176 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | ||
| 177 | } | ||
| 178 | }; | ||
| 179 | |||
| 180 | // we have key and iv, time to decrypt the "c" field, first prepare the decryptor | ||
| 147 | let cipher = match Aes128Cbc::new_var(&temp_key, &byte_iv) { | 181 | let cipher = match Aes128Cbc::new_var(&temp_key, &byte_iv) { |
| 148 | Ok(c) => c, | 182 | Ok(c) => c, |
| 149 | Err(err) => { | 183 | Err(err) => { |
| @@ -161,42 +195,49 @@ pub async fn authenticate_user( | |||
| 161 | } | 195 | } |
| 162 | }; | 196 | }; |
| 163 | 197 | ||
| 198 | // peel away the base64 from the auth packet | ||
| 164 | let auth_packet = match base64::decode(&request.c) { | 199 | let auth_packet = match base64::decode(&request.c) { |
| 165 | Ok(a) => a, | 200 | Ok(a) => a, |
| 166 | |||
| 167 | Err(err) => { | 201 | Err(err) => { |
| 168 | debug!( | 202 | debug!( |
| 169 | "The auth_packet (c field) did not base64 decode {} {}", | 203 | "\"c\" field of initial auth request was not base64 encoded: {}, {}", |
| 170 | &request.c, err | 204 | &request.c, err |
| 171 | ); | 205 | ); |
| 172 | 206 | ||
| 173 | let res_json = warp::reply::json(&GradeCoinResponse { | 207 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 174 | res: ResponseType::Error, | 208 | res: ResponseType::Error, |
| 175 | message: "The c field was not correctly base64 encoded".to_owned(), | 209 | message: format!( |
| 210 | "\"c\" field of initial auth request was not base64 encoded: {}, {}", | ||
| 211 | &request.c, err | ||
| 212 | ), | ||
| 176 | }); | 213 | }); |
| 177 | 214 | ||
| 178 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 215 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 179 | } | 216 | } |
| 180 | }; | 217 | }; |
| 181 | 218 | ||
| 219 | // c field was properly base64 encoded, now available in auth_packet | ||
| 220 | // decryptor was setup properly, with the correct lenght key | ||
| 182 | let mut buf = auth_packet.to_vec(); | 221 | let mut buf = auth_packet.to_vec(); |
| 183 | let auth_plaintext = match cipher.decrypt(&mut buf) { | 222 | let auth_plaintext = match cipher.decrypt(&mut buf) { |
| 184 | Ok(p) => p, | 223 | Ok(p) => p, |
| 185 | Err(err) => { | 224 | Err(err) => { |
| 186 | debug!( | 225 | println!( |
| 187 | "Base64 decoded auth request did not decrypt correctly {:?} {}", | 226 | "auth request (c) did not decrypt correctly {:?} {}", |
| 188 | &auth_packet, err | 227 | &auth_packet, err |
| 189 | ); | 228 | ); |
| 190 | 229 | ||
| 191 | let res_json = warp::reply::json(&GradeCoinResponse { | 230 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 192 | res: ResponseType::Error, | 231 | res: ResponseType::Error, |
| 193 | message: "The Base64 decoded auth request did not decrypt correctly".to_owned(), | 232 | message: "Failed to decrypt the 'c' field of the auth request, 'iv' and 'k_temp' were valid so far though" |
| 233 | .to_owned(), | ||
| 194 | }); | 234 | }); |
| 195 | 235 | ||
| 196 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 236 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 197 | } | 237 | } |
| 198 | }; | 238 | }; |
| 199 | 239 | ||
| 240 | // we have a decrypted c field, create a string from the bytes mess | ||
| 200 | let utf8_auth_plaintext = match String::from_utf8(auth_plaintext.to_vec()) { | 241 | let utf8_auth_plaintext = match String::from_utf8(auth_plaintext.to_vec()) { |
| 201 | Ok(text) => text, | 242 | Ok(text) => text, |
| 202 | Err(err) => { | 243 | Err(err) => { |
| @@ -207,13 +248,15 @@ pub async fn authenticate_user( | |||
| 207 | 248 | ||
| 208 | let res_json = warp::reply::json(&GradeCoinResponse { | 249 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 209 | res: ResponseType::Error, | 250 | res: ResponseType::Error, |
| 210 | message: "Auth plaintext couldn't get converted to UTF-8".to_owned(), | 251 | message: "P_AR couldn't get converted to UTF-8, please check your encoding" |
| 252 | .to_owned(), | ||
| 211 | }); | 253 | }); |
| 212 | 254 | ||
| 213 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 255 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 214 | } | 256 | } |
| 215 | }; | 257 | }; |
| 216 | 258 | ||
| 259 | // finally create an AuthRequest object from the plaintext | ||
| 217 | let request: AuthRequest = match serde_json::from_str(&utf8_auth_plaintext) { | 260 | let request: AuthRequest = match serde_json::from_str(&utf8_auth_plaintext) { |
| 218 | Ok(req) => req, | 261 | Ok(req) => req, |
| 219 | Err(err) => { | 262 | Err(err) => { |
| @@ -224,24 +267,32 @@ pub async fn authenticate_user( | |||
| 224 | 267 | ||
| 225 | let res_json = warp::reply::json(&GradeCoinResponse { | 268 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 226 | res: ResponseType::Error, | 269 | res: ResponseType::Error, |
| 227 | message: "The auth request JSON did not serialize correctly".to_owned(), | 270 | message: "The P_AR JSON did not serialize correctly, did it include all 3 fields 'student_id', 'passwd' and 'public_key'?".to_owned(), |
| 228 | }); | 271 | }); |
| 229 | 272 | ||
| 230 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 273 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 231 | } | 274 | } |
| 232 | }; | 275 | }; |
| 233 | 276 | ||
| 234 | let privileged_student_id = match MetuId::new(request.student_id, request.passwd) { | 277 | // is the student in AuthRequest privileged? |
| 235 | Some(id) => id, | 278 | let privileged_student_id = |
| 236 | None => { | 279 | match MetuId::new(request.student_id.clone(), request.passwd.clone()) { |
| 237 | let res_json = warp::reply::json(&GradeCoinResponse { | 280 | Some(id) => id, |
| 281 | None => { | ||
| 282 | debug!( | ||
| 283 | "Someone tried to auth with invalid credentials: {} {}", | ||
| 284 | &request.student_id, &request.passwd | ||
| 285 | ); | ||
| 286 | let res_json = warp::reply::json(&GradeCoinResponse { | ||
| 238 | res: ResponseType::Error, | 287 | res: ResponseType::Error, |
| 239 | message: "This user cannot have a gradecoin account".to_owned(), | 288 | message: |
| 289 | "The credentials given ('student_id', 'passwd') cannot hold a Gradecoin account" | ||
| 290 | .to_owned(), | ||
| 240 | }); | 291 | }); |
| 241 | 292 | ||
| 242 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 293 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 243 | } | 294 | } |
| 244 | }; | 295 | }; |
| 245 | 296 | ||
| 246 | // Students should be able to authenticate once | 297 | // Students should be able to authenticate once |
| 247 | { | 298 | { |
| @@ -264,7 +315,7 @@ pub async fn authenticate_user( | |||
| 264 | if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() { | 315 | if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() { |
| 265 | let res_json = warp::reply::json(&GradeCoinResponse { | 316 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 266 | res: ResponseType::Error, | 317 | res: ResponseType::Error, |
| 267 | message: "The supplied RSA public key is not in valid PEM format".to_owned(), | 318 | message: "The RSA 'public_key' in 'P_AR' is not in valid PEM format".to_owned(), |
| 268 | }); | 319 | }); |
| 269 | 320 | ||
| 270 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 321 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| @@ -279,23 +330,23 @@ pub async fn authenticate_user( | |||
| 279 | is_bot: false, | 330 | is_bot: false, |
| 280 | }; | 331 | }; |
| 281 | 332 | ||
| 282 | debug!("New user authenticated themselves! {:?}", &new_user); | 333 | debug!("NEW USER: {:?}", &new_user); |
| 283 | 334 | ||
| 335 | // save the user to disk | ||
| 284 | let user_at_rest_json = serde_json::to_string(&UserAtRest { | 336 | let user_at_rest_json = serde_json::to_string(&UserAtRest { |
| 337 | fingerprint: fingerprint.clone(), | ||
| 285 | user: User { | 338 | user: User { |
| 286 | user_id: new_user.user_id.clone(), | 339 | user_id: new_user.user_id.clone(), |
| 287 | public_key: new_user.public_key.clone(), | 340 | public_key: new_user.public_key.clone(), |
| 288 | balance: 0, | 341 | balance: new_user.balance, |
| 289 | is_bot: false, | 342 | is_bot: false, |
| 290 | }, | 343 | }, |
| 291 | fingerprint: fingerprint.clone(), | ||
| 292 | }) | 344 | }) |
| 293 | .unwrap(); | 345 | .unwrap(); |
| 294 | 346 | ||
| 295 | fs::write(format!("users/{}.guy", new_user.user_id), user_at_rest_json).unwrap(); | 347 | fs::write(format!("users/{}.guy", new_user.user_id), user_at_rest_json).unwrap(); |
| 296 | 348 | ||
| 297 | let mut userlist = db.users.write(); | 349 | let mut userlist = db.users.write(); |
| 298 | |||
| 299 | userlist.insert(fingerprint.clone(), new_user); | 350 | userlist.insert(fingerprint.clone(), new_user); |
| 300 | 351 | ||
| 301 | let res_json = warp::reply::json(&GradeCoinResponse { | 352 | let res_json = warp::reply::json(&GradeCoinResponse { |
| @@ -312,7 +363,6 @@ pub async fn authenticate_user( | |||
| 312 | /// GET /transaction | 363 | /// GET /transaction |
| 313 | /// Returns JSON array of transactions | 364 | /// Returns JSON array of transactions |
| 314 | pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> { | 365 | pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> { |
| 315 | debug!("GET /transaction, list_transactions() is handling"); | ||
| 316 | let mut result = HashMap::new(); | 366 | let mut result = HashMap::new(); |
| 317 | 367 | ||
| 318 | let transactions = db.pending_transactions.read(); | 368 | let transactions = db.pending_transactions.read(); |
| @@ -338,10 +388,9 @@ pub async fn propose_block( | |||
| 338 | token: String, | 388 | token: String, |
| 339 | db: Db, | 389 | db: Db, |
| 340 | ) -> Result<impl warp::Reply, warp::Rejection> { | 390 | ) -> Result<impl warp::Reply, warp::Rejection> { |
| 341 | debug!("POST /block, propose_block() is handling"); | ||
| 342 | |||
| 343 | warn!("New block proposal: {:?}", &new_block); | 391 | warn!("New block proposal: {:?}", &new_block); |
| 344 | 392 | ||
| 393 | // Check if there are enough transactions in the block | ||
| 345 | if new_block.transaction_list.len() < BLOCK_TRANSACTION_COUNT as usize { | 394 | if new_block.transaction_list.len() < BLOCK_TRANSACTION_COUNT as usize { |
| 346 | debug!( | 395 | debug!( |
| 347 | "{} transactions offered, needed {}", | 396 | "{} transactions offered, needed {}", |
| @@ -360,21 +409,19 @@ pub async fn propose_block( | |||
| 360 | } | 409 | } |
| 361 | 410 | ||
| 362 | // proposer (first transaction fingerprint) checks | 411 | // proposer (first transaction fingerprint) checks |
| 363 | |||
| 364 | let pending_transactions = db.pending_transactions.upgradable_read(); | 412 | let pending_transactions = db.pending_transactions.upgradable_read(); |
| 365 | 413 | ||
| 366 | let internal_user_fingerprint = match pending_transactions.get(&new_block.transaction_list[0]) { | 414 | let internal_user_fingerprint = match pending_transactions.get(&new_block.transaction_list[0]) { |
| 367 | Some(coinbase) => &coinbase.source, | 415 | Some(coinbase) => &coinbase.source, |
| 368 | None => { | 416 | None => { |
| 369 | debug!( | 417 | debug!( |
| 370 | "User with public key signature {:?} is not found in the database", | 418 | "Block proposer with public key signature {:?} is not found in the database", |
| 371 | new_block.transaction_list[0] | 419 | new_block.transaction_list[0] |
| 372 | ); | 420 | ); |
| 373 | 421 | ||
| 374 | let res_json = warp::reply::json(&GradeCoinResponse { | 422 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 375 | res: ResponseType::Error, | 423 | res: ResponseType::Error, |
| 376 | message: "User with that public key signature is not found in the database" | 424 | message: "Proposer of the first transaction is not found in the system".to_owned(), |
| 377 | .to_owned(), | ||
| 378 | }); | 425 | }); |
| 379 | 426 | ||
| 380 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 427 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| @@ -383,6 +430,7 @@ pub async fn propose_block( | |||
| 383 | 430 | ||
| 384 | let users_store = db.users.upgradable_read(); | 431 | let users_store = db.users.upgradable_read(); |
| 385 | 432 | ||
| 433 | // this probably cannot fail, if the transaction is valid then it must've been checked already | ||
| 386 | let internal_user = match users_store.get(internal_user_fingerprint) { | 434 | let internal_user = match users_store.get(internal_user_fingerprint) { |
| 387 | Some(existing_user) => existing_user, | 435 | Some(existing_user) => existing_user, |
| 388 | None => { | 436 | None => { |
| @@ -407,7 +455,7 @@ pub async fn propose_block( | |||
| 407 | let token_payload = match authorize_proposer(token, &proposer_public_key) { | 455 | let token_payload = match authorize_proposer(token, &proposer_public_key) { |
| 408 | Ok(data) => data, | 456 | Ok(data) => data, |
| 409 | Err(below) => { | 457 | Err(below) => { |
| 410 | debug!("Something went wrong below {:?}", below); | 458 | debug!("Something went wrong with the JWT {:?}", below); |
| 411 | 459 | ||
| 412 | let res_json = warp::reply::json(&GradeCoinResponse { | 460 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 413 | res: ResponseType::Error, | 461 | res: ResponseType::Error, |
| @@ -426,46 +474,36 @@ pub async fn propose_block( | |||
| 426 | ); | 474 | ); |
| 427 | let res_json = warp::reply::json(&GradeCoinResponse { | 475 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 428 | res: ResponseType::Error, | 476 | res: ResponseType::Error, |
| 429 | message: "The hash of the block did not match the hash given in JWT".to_owned(), | 477 | message: "The hash of the block did not match the hash given in JWT tha field" |
| 478 | .to_owned(), | ||
| 430 | }); | 479 | }); |
| 431 | 480 | ||
| 432 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 481 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 433 | } | 482 | } |
| 434 | 483 | ||
| 435 | // scope the HashSet | 484 | if !has_unique_elements(&new_block.transaction_list) { |
| 436 | { | 485 | debug!("Block contains duplicate transactions!"); |
| 437 | let mut proposed_transactions = HashSet::new(); | 486 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 438 | for tx in new_block.transaction_list.iter() { | 487 | res: ResponseType::Error, |
| 439 | proposed_transactions.insert(tx); | 488 | message: "Block cannot contain duplicate transactions".to_owned(), |
| 440 | } | 489 | }); |
| 441 | |||
| 442 | if proposed_transactions.len() < BLOCK_TRANSACTION_COUNT as usize { | ||
| 443 | let res_json = warp::reply::json(&GradeCoinResponse { | ||
| 444 | res: ResponseType::Error, | ||
| 445 | message: format!( | ||
| 446 | "Block cannot contain less than {} unique transaction(s).", | ||
| 447 | BLOCK_TRANSACTION_COUNT | ||
| 448 | ), | ||
| 449 | }); | ||
| 450 | 490 | ||
| 451 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 491 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 452 | } | ||
| 453 | } | 492 | } |
| 454 | 493 | ||
| 455 | // Scope the RwLocks, there are hashing stuff below | ||
| 456 | |||
| 457 | // Are transactions in the block valid? | 494 | // Are transactions in the block valid? |
| 458 | for transaction_hash in new_block.transaction_list.iter() { | 495 | for transaction_hash in new_block.transaction_list.iter() { |
| 459 | if !pending_transactions.contains_key(transaction_hash) { | 496 | if !pending_transactions.contains_key(transaction_hash) { |
| 460 | let res_json = warp::reply::json(&GradeCoinResponse { | 497 | let res_json = warp::reply::json(&GradeCoinResponse { |
| 461 | res: ResponseType::Error, | 498 | res: ResponseType::Error, |
| 462 | message: "Block contains unknown transaction".to_owned(), | 499 | message: "Block contains an unknown transaction".to_owned(), |
| 463 | }); | 500 | }); |
| 464 | 501 | ||
| 465 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 502 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
| 466 | } | 503 | } |
| 467 | } | 504 | } |
| 468 | 505 | ||
| 506 | // hash the block ourselves to double check | ||
| 469 | let naked_block = NakedBlock { | 507 | let naked_block = NakedBlock { |
| 470 | transaction_list: new_block.transaction_list.clone(), | 508 | transaction_list: new_block.transaction_list.clone(), |
| 471 | nonce: new_block.nonce, | 509 | nonce: new_block.nonce, |
| @@ -509,6 +547,7 @@ pub async fn propose_block( | |||
| 509 | let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); | 547 | let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); |
| 510 | let mut users_store = RwLockUpgradableReadGuard::upgrade(users_store); | 548 | let mut users_store = RwLockUpgradableReadGuard::upgrade(users_store); |
| 511 | 549 | ||
| 550 | // Play out the transactions | ||
| 512 | for fingerprint in new_block.transaction_list.iter() { | 551 | for fingerprint in new_block.transaction_list.iter() { |
| 513 | if let Some(transaction) = pending_transactions.remove(fingerprint) { | 552 | if let Some(transaction) = pending_transactions.remove(fingerprint) { |
| 514 | let source = &transaction.source; | 553 | let source = &transaction.source; |
| @@ -519,7 +558,7 @@ pub async fn propose_block( | |||
| 519 | } | 558 | } |
| 520 | 559 | ||
| 521 | if let Some(to) = users_store.get_mut(target) { | 560 | if let Some(to) = users_store.get_mut(target) { |
| 522 | to.balance += transaction.amount + 1; | 561 | to.balance += transaction.amount + TX_TRAFFIC_REWARD; |
| 523 | } | 562 | } |
| 524 | 563 | ||
| 525 | // if the receiver is a bot, they will reciprocate | 564 | // if the receiver is a bot, they will reciprocate |
| @@ -539,6 +578,7 @@ pub async fn propose_block( | |||
| 539 | } | 578 | } |
| 540 | } | 579 | } |
| 541 | 580 | ||
| 581 | // Reward the block proposer | ||
| 542 | let coinbase_fingerprint = new_block.transaction_list.get(0).unwrap(); | 582 | let coinbase_fingerprint = new_block.transaction_list.get(0).unwrap(); |
| 543 | 583 | ||
| 544 | if let Some(coinbase_user) = users_store.get_mut(coinbase_fingerprint) { | 584 | if let Some(coinbase_user) = users_store.get_mut(coinbase_fingerprint) { |
| @@ -562,7 +602,7 @@ pub async fn propose_block( | |||
| 562 | Ok(warp::reply::with_status( | 602 | Ok(warp::reply::with_status( |
| 563 | warp::reply::json(&GradeCoinResponse { | 603 | warp::reply::json(&GradeCoinResponse { |
| 564 | res: ResponseType::Success, | 604 | res: ResponseType::Success, |
| 565 | message: "Block accepted coinbase reward awarded".to_owned(), | 605 | message: "Block accepted, coinbase reward awarded".to_owned(), |
| 566 | }), | 606 | }), |
| 567 | StatusCode::CREATED, | 607 | StatusCode::CREATED, |
| 568 | )) | 608 | )) |
| @@ -582,8 +622,6 @@ pub async fn propose_transaction( | |||
| 582 | token: String, | 622 | token: String, |
| 583 | db: Db, | 623 | db: Db, |
| 584 | ) -> Result<impl warp::Reply, warp::Rejection> { | 624 | ) -> Result<impl warp::Reply, warp::Rejection> { |
| 585 | debug!("POST /transaction, propose_transaction() is handling"); | ||
| 586 | |||
| 587 | warn!("New transaction proposal: {:?}", &new_transaction); | 625 | warn!("New transaction proposal: {:?}", &new_transaction); |
| 588 | 626 | ||
| 589 | let users_store = db.users.read(); | 627 | let users_store = db.users.read(); |
| @@ -628,6 +666,25 @@ pub async fn propose_transaction( | |||
| 628 | } | 666 | } |
| 629 | }; | 667 | }; |
| 630 | 668 | ||
| 669 | // is the target of the transaction in the system? | ||
| 670 | if !users_store.contains_key(&new_transaction.target) { | ||
| 671 | debug!( | ||
| 672 | "Target of the transaction is not in the system {}", | ||
| 673 | new_transaction.target | ||
| 674 | ); | ||
| 675 | |||
| 676 | return Ok(warp::reply::with_status( | ||
| 677 | warp::reply::json(&GradeCoinResponse { | ||
| 678 | res: ResponseType::Error, | ||
| 679 | message: format!( | ||
| 680 | "Target of the transaction {} is not found in the system", | ||
| 681 | new_transaction.target | ||
| 682 | ), | ||
| 683 | }), | ||
| 684 | StatusCode::BAD_REQUEST, | ||
| 685 | )); | ||
| 686 | } | ||
| 687 | |||
| 631 | let transaction_id = calculate_transaction_id(&new_transaction.source, &new_transaction.target); | 688 | let transaction_id = calculate_transaction_id(&new_transaction.source, &new_transaction.target); |
| 632 | 689 | ||
| 633 | // OLD: Does this user have a pending transaction? | 690 | // OLD: Does this user have a pending transaction? |
| @@ -826,3 +883,12 @@ pub async fn user_list_handler(db: Db) -> Result<impl warp::Reply, warp::Rejecti | |||
| 826 | let res = template.render().unwrap(); | 883 | let res = template.render().unwrap(); |
| 827 | Ok(warp::reply::html(res)) | 884 | Ok(warp::reply::html(res)) |
| 828 | } | 885 | } |
| 886 | |||
| 887 | fn has_unique_elements<T>(iter: T) -> bool | ||
| 888 | where | ||
| 889 | T: IntoIterator, | ||
| 890 | T::Item: Eq + Hash, | ||
| 891 | { | ||
| 892 | let mut uniq = HashSet::new(); | ||
| 893 | iter.into_iter().all(move |x| uniq.insert(x)) | ||
| 894 | } | ||
