aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYigit Sever2021-04-14 21:28:37 +0300
committerYigit Sever2021-04-14 21:28:37 +0300
commitc0cbe39d220090c9c3b3fd778b926956dae753dd (patch)
treea6a6786cf235f3c334aa4bc3b87db05b37da3ae3 /src
parent80f12505a2c6cd1ccded88f9bdc5f6caa2c5ab32 (diff)
downloadgradecoin-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.rs105
-rw-r--r--src/schema.rs9
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;
18use warp::{http::StatusCode, reply}; 18use warp::{http::StatusCode, reply};
19 19
20use crate::PRIVATE_KEY; 20use crate::PRIVATE_KEY;
21const BLOCK_TRANSACTION_COUNT: u8 = 10;
21 22
22// Encryption primitive 23// Encryption primitive
23type Aes128Cbc = Cbc<Aes128, Pkcs7>; 24type 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> //
232pub async fn authorized_propose_block( 231pub 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
39fn read_block_name() -> io::Result<Vec<PathBuf>> { 39fn 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)]
96pub struct Db { 93pub 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)]
171pub struct MetuId { 168pub struct MetuId {
172 id: String, 169 id: String,