diff options
author | Yigit Sever | 2021-04-14 21:28:37 +0300 |
---|---|---|
committer | Yigit Sever | 2021-04-14 21:28:37 +0300 |
commit | c0cbe39d220090c9c3b3fd778b926956dae753dd (patch) | |
tree | a6a6786cf235f3c334aa4bc3b87db05b37da3ae3 /src | |
parent | 80f12505a2c6cd1ccded88f9bdc5f6caa2c5ab32 (diff) | |
download | gradecoin-c0cbe39d220090c9c3b3fd778b926956dae753dd.tar.gz gradecoin-c0cbe39d220090c9c3b3fd778b926956dae753dd.tar.bz2 gradecoin-c0cbe39d220090c9c3b3fd778b926956dae753dd.zip |
Accepted block transactions play out now
Diffstat (limited to 'src')
-rw-r--r-- | src/handlers.rs | 105 | ||||
-rw-r--r-- | src/schema.rs | 9 |
2 files changed, 75 insertions, 39 deletions
diff --git a/src/handlers.rs b/src/handlers.rs index 4a1edcc..420e82a 100644 --- a/src/handlers.rs +++ b/src/handlers.rs | |||
@@ -18,6 +18,7 @@ use std::fs; | |||
18 | use warp::{http::StatusCode, reply}; | 18 | use warp::{http::StatusCode, reply}; |
19 | 19 | ||
20 | use crate::PRIVATE_KEY; | 20 | use crate::PRIVATE_KEY; |
21 | const BLOCK_TRANSACTION_COUNT: u8 = 10; | ||
21 | 22 | ||
22 | // Encryption primitive | 23 | // Encryption primitive |
23 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; | 24 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; |
@@ -116,7 +117,7 @@ pub async fn authenticate_user( | |||
116 | 117 | ||
117 | let provided_id = request.student_id.clone(); | 118 | let provided_id = request.student_id.clone(); |
118 | 119 | ||
119 | let privileged_student_id = match MetuId::new(request.student_id) { | 120 | let privileged_student_id = match MetuId::new(request.student_id, request.passwd) { |
120 | Some(id) => id, | 121 | Some(id) => id, |
121 | None => { | 122 | None => { |
122 | let res_json = warp::reply::json(&GradeCoinResponse { | 123 | let res_json = warp::reply::json(&GradeCoinResponse { |
@@ -223,12 +224,10 @@ pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> { | |||
223 | /// Proposes a new block for the next round. | 224 | /// Proposes a new block for the next round. |
224 | /// Can reject the block | 225 | /// Can reject the block |
225 | /// | 226 | /// |
226 | /// TODO: WHO IS PROPOSING THIS BLOCK OH GOD <13-04-21, yigit> // ok let's say the proposer has | 227 | /// The proposer has to put their transaction as the first transaction of the [`transaction_list`]. |
227 | /// to put their transaction as the first transaction of the transaction_list | 228 | /// This is the analogue of `coinbase` in Bitcoin works |
228 | /// that's not going to backfire in any way | ||
229 | /// | 229 | /// |
230 | /// TODO: after a block is accepted, it's transactions should play out and the proposer should | 230 | /// The `coinbase` transaction also gets something for their efforts. |
231 | /// get something for their efforts <13-04-21, yigit> // | ||
232 | pub async fn authorized_propose_block( | 231 | pub async fn authorized_propose_block( |
233 | new_block: Block, | 232 | new_block: Block, |
234 | token: String, | 233 | token: String, |
@@ -236,10 +235,23 @@ pub async fn authorized_propose_block( | |||
236 | ) -> Result<impl warp::Reply, warp::Rejection> { | 235 | ) -> Result<impl warp::Reply, warp::Rejection> { |
237 | debug!("POST request to /block, authorized_propose_block"); | 236 | debug!("POST request to /block, authorized_propose_block"); |
238 | 237 | ||
239 | let users_store = db.users.read(); | 238 | let users_store = db.users.upgradable_read(); |
240 | 239 | ||
241 | println!("{:?}", &new_block); | 240 | println!("{:?}", &new_block); |
242 | 241 | ||
242 | if new_block.transaction_list.len() < 1 { | ||
243 | let res_json = warp::reply::json(&GradeCoinResponse { | ||
244 | res: ResponseType::Error, | ||
245 | message: format!( | ||
246 | "There should be {} transactions in the block", | ||
247 | BLOCK_TRANSACTION_COUNT | ||
248 | ), | ||
249 | }); | ||
250 | |||
251 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | ||
252 | } | ||
253 | |||
254 | // proposer (first transaction fingerprint) checks | ||
243 | let internal_user = match users_store.get(&new_block.transaction_list[0]) { | 255 | let internal_user = match users_store.get(&new_block.transaction_list[0]) { |
244 | Some(existing_user) => existing_user, | 256 | Some(existing_user) => existing_user, |
245 | None => { | 257 | None => { |
@@ -250,7 +262,7 @@ pub async fn authorized_propose_block( | |||
250 | 262 | ||
251 | let res_json = warp::reply::json(&GradeCoinResponse { | 263 | let res_json = warp::reply::json(&GradeCoinResponse { |
252 | res: ResponseType::Error, | 264 | res: ResponseType::Error, |
253 | message: "User with the given public key signature is not found in the database" | 265 | message: "User with that public key signature is not found in the database" |
254 | .to_owned(), | 266 | .to_owned(), |
255 | }); | 267 | }); |
256 | 268 | ||
@@ -260,6 +272,7 @@ pub async fn authorized_propose_block( | |||
260 | 272 | ||
261 | let proposer_public_key = &internal_user.public_key; | 273 | let proposer_public_key = &internal_user.public_key; |
262 | 274 | ||
275 | // JWT Check | ||
263 | let token_payload = match authorize_proposer(token, &proposer_public_key) { | 276 | let token_payload = match authorize_proposer(token, &proposer_public_key) { |
264 | Ok(data) => data, | 277 | Ok(data) => data, |
265 | Err(below) => { | 278 | Err(below) => { |
@@ -274,8 +287,7 @@ pub async fn authorized_propose_block( | |||
274 | } | 287 | } |
275 | }; | 288 | }; |
276 | 289 | ||
277 | debug!("authorized for block proposal"); | 290 | // Block hash check |
278 | |||
279 | if token_payload.claims.tha != new_block.hash { | 291 | if token_payload.claims.tha != new_block.hash { |
280 | debug!( | 292 | debug!( |
281 | "The Hash of the block {:?} did not match the hash given in jwt {:?}", | 293 | "The Hash of the block {:?} did not match the hash given in jwt {:?}", |
@@ -289,18 +301,20 @@ pub async fn authorized_propose_block( | |||
289 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 301 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
290 | } | 302 | } |
291 | 303 | ||
292 | debug!("clear for block proposal"); | 304 | // Scope the RwLocks, there are hashing stuff below |
293 | let pending_transactions = db.pending_transactions.upgradable_read(); | 305 | { |
294 | let blockchain = db.blockchain.upgradable_read(); | 306 | let pending_transactions = db.pending_transactions.read(); |
295 | 307 | ||
296 | for transaction_hash in new_block.transaction_list.iter() { | 308 | // Are transactions in the block valid? |
297 | if !pending_transactions.contains_key(transaction_hash) { | 309 | for transaction_hash in new_block.transaction_list.iter() { |
298 | let res_json = warp::reply::json(&GradeCoinResponse { | 310 | if !pending_transactions.contains_key(transaction_hash) { |
299 | res: ResponseType::Error, | 311 | let res_json = warp::reply::json(&GradeCoinResponse { |
300 | message: "Block contains unknown transaction".to_owned(), | 312 | res: ResponseType::Error, |
301 | }); | 313 | message: "Block contains unknown transaction".to_owned(), |
314 | }); | ||
302 | 315 | ||
303 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 316 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
317 | } | ||
304 | } | 318 | } |
305 | } | 319 | } |
306 | 320 | ||
@@ -315,7 +329,7 @@ pub async fn authorized_propose_block( | |||
315 | let hashvalue = Blake2s::digest(&naked_block_flat); | 329 | let hashvalue = Blake2s::digest(&naked_block_flat); |
316 | let hash_string = format!("{:x}", hashvalue); | 330 | let hash_string = format!("{:x}", hashvalue); |
317 | 331 | ||
318 | // Does the hash claimed in block matched with the actual hash? | 332 | // Does the hash claimed in block match with the actual hash? |
319 | if hash_string != new_block.hash { | 333 | if hash_string != new_block.hash { |
320 | debug!("request was not telling the truth, hash values do not match"); | 334 | debug!("request was not telling the truth, hash values do not match"); |
321 | let res_json = warp::reply::json(&GradeCoinResponse { | 335 | let res_json = warp::reply::json(&GradeCoinResponse { |
@@ -339,7 +353,32 @@ pub async fn authorized_propose_block( | |||
339 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); | 353 | return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); |
340 | } | 354 | } |
341 | 355 | ||
342 | let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); | 356 | // All clear, block accepted! |
357 | debug!("We have a new block!"); | ||
358 | |||
359 | { | ||
360 | let pending_transactions = db.pending_transactions.read(); | ||
361 | let mut users = db.users.write(); | ||
362 | |||
363 | for fingerprint in new_block.transaction_list.iter() { | ||
364 | let transaction = pending_transactions.get(fingerprint).unwrap(); | ||
365 | let source = &transaction.source; | ||
366 | let target = &transaction.target; | ||
367 | |||
368 | if let Some(from) = users.get_mut(source) { | ||
369 | from.balance -= transaction.amount; | ||
370 | } | ||
371 | |||
372 | if let Some(to) = users.get_mut(target) { | ||
373 | to.balance += transaction.amount; | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | { | ||
379 | let mut pending_transactions = db.pending_transactions.write(); | ||
380 | pending_transactions.clear(); | ||
381 | } | ||
343 | 382 | ||
344 | let block_json = serde_json::to_string(&new_block).unwrap(); | 383 | let block_json = serde_json::to_string(&new_block).unwrap(); |
345 | 384 | ||
@@ -349,15 +388,15 @@ pub async fn authorized_propose_block( | |||
349 | ) | 388 | ) |
350 | .unwrap(); | 389 | .unwrap(); |
351 | 390 | ||
352 | *blockchain = new_block; | 391 | { |
353 | 392 | let mut blockchain = db.blockchain.write(); | |
354 | let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); | 393 | *blockchain = new_block; |
355 | pending_transactions.clear(); | 394 | } |
356 | 395 | ||
357 | Ok(warp::reply::with_status( | 396 | Ok(warp::reply::with_status( |
358 | warp::reply::json(&GradeCoinResponse { | 397 | warp::reply::json(&GradeCoinResponse { |
359 | res: ResponseType::Success, | 398 | res: ResponseType::Success, |
360 | message: "Block accepted".to_owned(), | 399 | message: "Block accepted coinbase reward awarded".to_owned(), |
361 | }), | 400 | }), |
362 | StatusCode::CREATED, | 401 | StatusCode::CREATED, |
363 | )) | 402 | )) |
@@ -379,7 +418,6 @@ pub async fn authorized_propose_transaction( | |||
379 | db: Db, | 418 | db: Db, |
380 | ) -> Result<impl warp::Reply, warp::Rejection> { | 419 | ) -> Result<impl warp::Reply, warp::Rejection> { |
381 | debug!("POST request to /transaction, authorized_propose_transaction"); | 420 | debug!("POST request to /transaction, authorized_propose_transaction"); |
382 | debug!("The transaction request: {:?}", new_transaction); | ||
383 | 421 | ||
384 | let users_store = db.users.read(); | 422 | let users_store = db.users.read(); |
385 | 423 | ||
@@ -403,7 +441,7 @@ pub async fn authorized_propose_transaction( | |||
403 | } | 441 | } |
404 | }; | 442 | }; |
405 | 443 | ||
406 | // `user` is an authenticated student, can propose | 444 | // `internal_user` is an authenticated student, can propose |
407 | 445 | ||
408 | // check if user can afford the transaction | 446 | // check if user can afford the transaction |
409 | if new_transaction.by == new_transaction.source { | 447 | if new_transaction.by == new_transaction.source { |
@@ -418,7 +456,7 @@ pub async fn authorized_propose_transaction( | |||
418 | )); | 456 | )); |
419 | } | 457 | } |
420 | } else { | 458 | } else { |
421 | // todo: add bank mechanism | 459 | // TODO: add bank mechanism <14-04-21, keles> // |
422 | return Ok(warp::reply::with_status( | 460 | return Ok(warp::reply::with_status( |
423 | warp::reply::json(&GradeCoinResponse { | 461 | warp::reply::json(&GradeCoinResponse { |
424 | res: ResponseType::Error, | 462 | res: ResponseType::Error, |
@@ -446,9 +484,10 @@ pub async fn authorized_propose_transaction( | |||
446 | } | 484 | } |
447 | }; | 485 | }; |
448 | 486 | ||
449 | // this transaction was already checked for correctness at custom_filters, we can panic | 487 | // authorized for transaction proposal |
450 | // here if it has been changed since | 488 | |
451 | debug!("authorized for transaction proposal"); | 489 | // this transaction was already checked for correctness at custom_filters, we can panic here if |
490 | // it has been changed since | ||
452 | 491 | ||
453 | let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap()); | 492 | let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap()); |
454 | 493 | ||
diff --git a/src/schema.rs b/src/schema.rs index 6402724..2a9e1db 100644 --- a/src/schema.rs +++ b/src/schema.rs | |||
@@ -37,7 +37,7 @@ fn last_block_exists() -> (bool, String) { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | fn read_block_name() -> io::Result<Vec<PathBuf>> { | 39 | fn read_block_name() -> io::Result<Vec<PathBuf>> { |
40 | let mut entries = fs::read_dir("./blocks")? | 40 | let entries = fs::read_dir("./blocks")? |
41 | .map(|res| res.map(|e| e.path())) | 41 | .map(|res| res.map(|e| e.path())) |
42 | .collect::<Result<Vec<_>, io::Error>>()?; | 42 | .collect::<Result<Vec<_>, io::Error>>()?; |
43 | 43 | ||
@@ -89,13 +89,10 @@ pub struct Claims { | |||
89 | /// | 89 | /// |
90 | /// [`Db::users`] is the in memory representation of the users, with their public keys, metu_ids and | 90 | /// [`Db::users`] is the in memory representation of the users, with their public keys, metu_ids and |
91 | /// gradecoin balances. | 91 | /// gradecoin balances. |
92 | /// | ||
93 | /// TODO: Replace the pending_transactions HashMap<String, Transaction> with | ||
94 | /// HashMap<Fingerprint, Transaction> | ||
95 | #[derive(Debug, Clone)] | 92 | #[derive(Debug, Clone)] |
96 | pub struct Db { | 93 | pub struct Db { |
97 | pub blockchain: Arc<RwLock<Block>>, | 94 | pub blockchain: Arc<RwLock<Block>>, |
98 | pub pending_transactions: Arc<RwLock<HashMap<String, Transaction>>>, | 95 | pub pending_transactions: Arc<RwLock<HashMap<Fingerprint, Transaction>>>, |
99 | pub users: Arc<RwLock<HashMap<String, User>>>, | 96 | pub users: Arc<RwLock<HashMap<String, User>>>, |
100 | } | 97 | } |
101 | 98 | ||
@@ -166,7 +163,7 @@ pub struct User { | |||
166 | pub balance: i32, | 163 | pub balance: i32, |
167 | } | 164 | } |
168 | 165 | ||
169 | /// The values will be hard coded so MetuId::new() can accept/reject values based on that | 166 | /// The values are hard coded in [`OUR_STUDENTS`] so MetuId::new() can accept/reject values based on that |
170 | #[derive(Serialize, Deserialize, Debug, PartialEq)] | 167 | #[derive(Serialize, Deserialize, Debug, PartialEq)] |
171 | pub struct MetuId { | 168 | pub struct MetuId { |
172 | id: String, | 169 | id: String, |