diff options
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, |
