From 8b50bf2e66f02e64e7fbec8079094bafb5ccdc6a Mon Sep 17 00:00:00 2001 From: Yigit Sever Date: Tue, 13 Apr 2021 04:51:19 +0300 Subject: Require authorization for Block POST Not tested because it's impossible to follow without verbose error messages, failing 1 test --- src/handlers.rs | 142 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 50 deletions(-) (limited to 'src/handlers.rs') diff --git a/src/handlers.rs b/src/handlers.rs index 80ed1f7..b896ac2 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -81,75 +81,101 @@ pub async fn list_transactions(db: Db) -> Result { Ok(reply::with_status(reply::json(&result), StatusCode::OK)) } -/// GET /block -/// Returns JSON array of blocks -/// Cannot fail -/// Mostly around for debug purposes -pub async fn list_blocks(db: Db) -> Result { - debug!("GET request to /block, list_blocks"); - - let block = db.blockchain.read(); - - Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) -} - /// POST /block /// Proposes a new block for the next round /// Can reject the block -pub async fn propose_block(new_block: Block, db: Db) -> Result { - debug!("new block request {:?}", new_block); +pub async fn auth_propose_block( + new_block: Block, + token: String, + db: Db, +) -> Result { + debug!("POST request to /block, auth_propose_block"); - // https://blog.logrocket.com/create-an-async-crud-web-service-in-rust-with-warp/ (this has - // error.rs, error struct, looks very clean) + // Authorization check + let raw_jwt = token.trim_start_matches(BEARER).to_owned(); + debug!("raw_jwt: {:?}", raw_jwt); - let pending_transactions = db.pending_transactions.upgradable_read(); - let blockchain = db.blockchain.upgradable_read(); + // TODO: WHO IS PROPOSING THIS BLOCK OH GOD <13-04-21, yigit> // ok let's say the proposer has + // to put their transaction as the first transaction of the transaction_list + // that's not going to backfire in any way + // TODO: after a block is accepted, it's transactions should play out and the proposer should + // get something for their efforts <13-04-21, yigit> // + if let Some(user) = db.users.read().get(&new_block.transaction_list[0]) { + let proposer_public_key = &user.public_key; - // check 1, new_block.transaction_list from pending_transactions pool? <07-04-21, yigit> // - for transaction_hash in new_block.transaction_list.iter() { - if !pending_transactions.contains_key(transaction_hash) { - return Ok(StatusCode::BAD_REQUEST); - } - } + if let Ok(decoded) = decode::( + &raw_jwt, + &DecodingKey::from_rsa_pem(proposer_public_key.as_bytes()).unwrap(), + &Validation::new(Algorithm::RS256), + ) { + if decoded.claims.tha != new_block.hash { + debug!("Authorization unsuccessful"); + return Ok(StatusCode::BAD_REQUEST); + } - let naked_block = NakedBlock { - transaction_list: new_block.transaction_list.clone(), - nonce: new_block.nonce.clone(), - timestamp: new_block.timestamp.clone(), - }; + debug!("authorized for block proposal"); - let naked_block_flat = serde_json::to_vec(&naked_block).unwrap(); + let pending_transactions = db.pending_transactions.upgradable_read(); + let blockchain = db.blockchain.upgradable_read(); - let hashvalue = Blake2s::digest(&naked_block_flat); - let hash_string = format!("{:x}", hashvalue); + for transaction_hash in new_block.transaction_list.iter() { + if !pending_transactions.contains_key(transaction_hash) { + return Ok(StatusCode::BAD_REQUEST); + } + } - // 6 rightmost bits are zero - let should_zero = hashvalue[31] as i32 + hashvalue[30] as i32 + hashvalue[29] as i32; + let naked_block = NakedBlock { + transaction_list: new_block.transaction_list.clone(), + nonce: new_block.nonce.clone(), + timestamp: new_block.timestamp.clone(), + }; + + let naked_block_flat = serde_json::to_vec(&naked_block).unwrap(); + + let hashvalue = Blake2s::digest(&naked_block_flat); + let hash_string = format!("{:x}", hashvalue); - if should_zero == 0 { - // one last check to see if block is telling the truth - if hash_string == new_block.hash { - let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); + // 6 rightmost bits are zero? + let should_zero = hashvalue[31] as i32 + hashvalue[30] as i32 + hashvalue[29] as i32; - let block_json = serde_json::to_string(&new_block).unwrap(); + if should_zero == 0 { + // one last check to see if block is telling the truth + if hash_string == new_block.hash { + let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); - fs::write( - format!("blocks/{}.block", new_block.timestamp.timestamp()), - block_json, - ) - .unwrap(); + let block_json = serde_json::to_string(&new_block).unwrap(); - *blockchain = new_block; + fs::write( + format!("blocks/{}.block", new_block.timestamp.timestamp()), + block_json, + ) + .unwrap(); - let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); - pending_transactions.clear(); + *blockchain = new_block; - Ok(StatusCode::CREATED) + let mut pending_transactions = + RwLockUpgradableReadGuard::upgrade(pending_transactions); + pending_transactions.clear(); + + Ok(StatusCode::CREATED) + } else { + debug!("request was not telling the truth, hash values do not match"); + // TODO: does this condition make more sense _before_ the hash 0s check? <13-04-21, yigit> // + Ok(StatusCode::BAD_REQUEST) + } + } else { + debug!("the hash does not have 6 rightmost zero bits"); + Ok(StatusCode::BAD_REQUEST) + } } else { + debug!("authorization failed"); Ok(StatusCode::BAD_REQUEST) } } else { - // reject + debug!( + "A user with public key signature {:?} is not found in the database", + new_block.transaction_list[0] + ); Ok(StatusCode::BAD_REQUEST) } } @@ -164,6 +190,8 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result Result { + debug!("GET request to /block, list_blocks"); + + let block = db.blockchain.read(); + + Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) +} -- cgit v1.2.3-70-g09d2