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 | } | ||