aboutsummaryrefslogtreecommitdiffstats
path: root/src/handlers.rs
diff options
context:
space:
mode:
authorYigit Sever2021-04-13 04:51:19 +0300
committerYigit Sever2021-04-13 04:51:19 +0300
commit116af4059f43eb3986b54fee080af9f67c247472 (patch)
tree387c03347f7d72fe949861518cfc606759b4c4a2 /src/handlers.rs
parent14b6904f00a17d3d6757a7e5222df966aa37fa35 (diff)
downloadgradecoin-116af4059f43eb3986b54fee080af9f67c247472.tar.gz
gradecoin-116af4059f43eb3986b54fee080af9f67c247472.tar.bz2
gradecoin-116af4059f43eb3986b54fee080af9f67c247472.zip
Require authorization for Block POST
Not tested because it's impossible to follow without verbose error messages, failing 1 test
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs142
1 files changed, 92 insertions, 50 deletions
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<impl warp::Reply, Infallible> {
81 Ok(reply::with_status(reply::json(&result), StatusCode::OK)) 81 Ok(reply::with_status(reply::json(&result), StatusCode::OK))
82} 82}
83 83
84/// GET /block
85/// Returns JSON array of blocks
86/// Cannot fail
87/// Mostly around for debug purposes
88pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> {
89 debug!("GET request to /block, list_blocks");
90
91 let block = db.blockchain.read();
92
93 Ok(reply::with_status(reply::json(&*block), StatusCode::OK))
94}
95
96/// POST /block 84/// POST /block
97/// Proposes a new block for the next round 85/// Proposes a new block for the next round
98/// Can reject the block 86/// Can reject the block
99pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply, warp::Rejection> { 87pub async fn auth_propose_block(
100 debug!("new block request {:?}", new_block); 88 new_block: Block,
89 token: String,
90 db: Db,
91) -> Result<impl warp::Reply, warp::Rejection> {
92 debug!("POST request to /block, auth_propose_block");
101 93
102 // https://blog.logrocket.com/create-an-async-crud-web-service-in-rust-with-warp/ (this has 94 // Authorization check
103 // error.rs, error struct, looks very clean) 95 let raw_jwt = token.trim_start_matches(BEARER).to_owned();
96 debug!("raw_jwt: {:?}", raw_jwt);
104 97
105 let pending_transactions = db.pending_transactions.upgradable_read(); 98 // TODO: WHO IS PROPOSING THIS BLOCK OH GOD <13-04-21, yigit> // ok let's say the proposer has
106 let blockchain = db.blockchain.upgradable_read(); 99 // to put their transaction as the first transaction of the transaction_list
100 // that's not going to backfire in any way
101 // TODO: after a block is accepted, it's transactions should play out and the proposer should
102 // get something for their efforts <13-04-21, yigit> //
103 if let Some(user) = db.users.read().get(&new_block.transaction_list[0]) {
104 let proposer_public_key = &user.public_key;
107 105
108 // check 1, new_block.transaction_list from pending_transactions pool? <07-04-21, yigit> // 106 if let Ok(decoded) = decode::<Claims>(
109 for transaction_hash in new_block.transaction_list.iter() { 107 &raw_jwt,
110 if !pending_transactions.contains_key(transaction_hash) { 108 &DecodingKey::from_rsa_pem(proposer_public_key.as_bytes()).unwrap(),
111 return Ok(StatusCode::BAD_REQUEST); 109 &Validation::new(Algorithm::RS256),
112 } 110 ) {
113 } 111 if decoded.claims.tha != new_block.hash {
112 debug!("Authorization unsuccessful");
113 return Ok(StatusCode::BAD_REQUEST);
114 }
114 115
115 let naked_block = NakedBlock { 116 debug!("authorized for block proposal");
116 transaction_list: new_block.transaction_list.clone(),
117 nonce: new_block.nonce.clone(),
118 timestamp: new_block.timestamp.clone(),
119 };
120 117
121 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap(); 118 let pending_transactions = db.pending_transactions.upgradable_read();
119 let blockchain = db.blockchain.upgradable_read();
122 120
123 let hashvalue = Blake2s::digest(&naked_block_flat); 121 for transaction_hash in new_block.transaction_list.iter() {
124 let hash_string = format!("{:x}", hashvalue); 122 if !pending_transactions.contains_key(transaction_hash) {
123 return Ok(StatusCode::BAD_REQUEST);
124 }
125 }
125 126
126 // 6 rightmost bits are zero 127 let naked_block = NakedBlock {
127 let should_zero = hashvalue[31] as i32 + hashvalue[30] as i32 + hashvalue[29] as i32; 128 transaction_list: new_block.transaction_list.clone(),
129 nonce: new_block.nonce.clone(),
130 timestamp: new_block.timestamp.clone(),
131 };
132
133 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap();
134
135 let hashvalue = Blake2s::digest(&naked_block_flat);
136 let hash_string = format!("{:x}", hashvalue);
128 137
129 if should_zero == 0 { 138 // 6 rightmost bits are zero?
130 // one last check to see if block is telling the truth 139 let should_zero = hashvalue[31] as i32 + hashvalue[30] as i32 + hashvalue[29] as i32;
131 if hash_string == new_block.hash {
132 let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain);
133 140
134 let block_json = serde_json::to_string(&new_block).unwrap(); 141 if should_zero == 0 {
142 // one last check to see if block is telling the truth
143 if hash_string == new_block.hash {
144 let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain);
135 145
136 fs::write( 146 let block_json = serde_json::to_string(&new_block).unwrap();
137 format!("blocks/{}.block", new_block.timestamp.timestamp()),
138 block_json,
139 )
140 .unwrap();
141 147
142 *blockchain = new_block; 148 fs::write(
149 format!("blocks/{}.block", new_block.timestamp.timestamp()),
150 block_json,
151 )
152 .unwrap();
143 153
144 let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); 154 *blockchain = new_block;
145 pending_transactions.clear();
146 155
147 Ok(StatusCode::CREATED) 156 let mut pending_transactions =
157 RwLockUpgradableReadGuard::upgrade(pending_transactions);
158 pending_transactions.clear();
159
160 Ok(StatusCode::CREATED)
161 } else {
162 debug!("request was not telling the truth, hash values do not match");
163 // TODO: does this condition make more sense _before_ the hash 0s check? <13-04-21, yigit> //
164 Ok(StatusCode::BAD_REQUEST)
165 }
166 } else {
167 debug!("the hash does not have 6 rightmost zero bits");
168 Ok(StatusCode::BAD_REQUEST)
169 }
148 } else { 170 } else {
171 debug!("authorization failed");
149 Ok(StatusCode::BAD_REQUEST) 172 Ok(StatusCode::BAD_REQUEST)
150 } 173 }
151 } else { 174 } else {
152 // reject 175 debug!(
176 "A user with public key signature {:?} is not found in the database",
177 new_block.transaction_list[0]
178 );
153 Ok(StatusCode::BAD_REQUEST) 179 Ok(StatusCode::BAD_REQUEST)
154 } 180 }
155} 181}
@@ -164,6 +190,8 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply,
164/// * `db` - Global [`Db`] instance 190/// * `db` - Global [`Db`] instance
165/// 191///
166/// TODO This method should check if the user has enough balance for the transaction 192/// TODO This method should check if the user has enough balance for the transaction
193///
194/// TODO: refactor this https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html
167pub async fn auth_propose_transaction( 195pub async fn auth_propose_transaction(
168 new_transaction: Transaction, 196 new_transaction: Transaction,
169 token: String, 197 token: String,
@@ -173,8 +201,9 @@ pub async fn auth_propose_transaction(
173 debug!("The transaction request: {:?}", new_transaction); 201 debug!("The transaction request: {:?}", new_transaction);
174 202
175 let raw_jwt = token.trim_start_matches(BEARER).to_owned(); 203 let raw_jwt = token.trim_start_matches(BEARER).to_owned();
176 debug!("raw_jwt: {:?}", raw_jwt); 204 println!("raw_jwt: {:?}", raw_jwt);
177 205
206 // Authorization check first
178 if let Some(user) = db.users.read().get(&new_transaction.by) { 207 if let Some(user) = db.users.read().get(&new_transaction.by) {
179 // This public key was already written to the database, we can panic if it's not valid at 208 // This public key was already written to the database, we can panic if it's not valid at
180 // *this* point 209 // *this* point
@@ -187,6 +216,7 @@ pub async fn auth_propose_transaction(
187 ) { 216 ) {
188 // this transaction was already checked for correctness at custom_filters, we can panic 217 // this transaction was already checked for correctness at custom_filters, we can panic
189 // here if it has been changed since 218 // here if it has been changed since
219 debug!("authorized for transaction proposal");
190 220
191 let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap()); 221 let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap());
192 222
@@ -215,3 +245,15 @@ pub async fn auth_propose_transaction(
215 Ok(StatusCode::BAD_REQUEST) 245 Ok(StatusCode::BAD_REQUEST)
216 } 246 }
217} 247}
248
249/// GET /block
250/// Returns JSON array of blocks
251/// Cannot fail
252/// Mostly around for debug purposes
253pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> {
254 debug!("GET request to /block, list_blocks");
255
256 let block = db.blockchain.read();
257
258 Ok(reply::with_status(reply::json(&*block), StatusCode::OK))
259}