aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/custom_filters.rs8
-rw-r--r--src/handlers.rs54
-rw-r--r--src/lib.rs9
-rw-r--r--src/main.rs6
-rw-r--r--src/routes.rs80
-rw-r--r--src/schema.rs12
6 files changed, 133 insertions, 36 deletions
diff --git a/src/custom_filters.rs b/src/custom_filters.rs
index 0806c6d..315ba4a 100644
--- a/src/custom_filters.rs
+++ b/src/custom_filters.rs
@@ -1,10 +1,7 @@
1// Common filters ment to be shared between many endpoints 1use gradecoin::schema::{AuthRequest, Block, Db, Transaction};
2
3use std::convert::Infallible; 2use std::convert::Infallible;
4use warp::{Filter, Rejection}; 3use warp::{Filter, Rejection};
5 4
6use crate::schema::{Block, Db, Transaction, AuthRequest};
7
8// Database context for routes 5// Database context for routes
9pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clone { 6pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clone {
10 warp::any().map(move || db.clone()) 7 warp::any().map(move || db.clone())
@@ -12,7 +9,8 @@ pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clo
12 9
13// Accept only json encoded User body and reject big payloads 10// Accept only json encoded User body and reject big payloads
14// TODO: find a good limit for this, (=e2482057; 8 char String + rsa pem) <11-04-21, yigit> // 11// TODO: find a good limit for this, (=e2482057; 8 char String + rsa pem) <11-04-21, yigit> //
15pub fn auth_request_json_body() -> impl Filter<Extract = (AuthRequest,), Error = Rejection> + Clone { 12pub fn auth_request_json_body() -> impl Filter<Extract = (AuthRequest,), Error = Rejection> + Clone
13{
16 warp::body::content_length_limit(1024 * 32).and(warp::body::json()) 14 warp::body::content_length_limit(1024 * 32).and(warp::body::json())
17} 15}
18 16
diff --git a/src/handlers.rs b/src/handlers.rs
index bfd57bc..6edc96f 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -2,12 +2,15 @@
2use log::debug; 2use log::debug;
3use parking_lot::RwLockUpgradableReadGuard; 3use parking_lot::RwLockUpgradableReadGuard;
4use serde_json; 4use serde_json;
5use serde_json::json;
5use std::convert::Infallible; 6use std::convert::Infallible;
6use warp::{http::Response, http::StatusCode, reply}; 7use warp::{http::Response, http::StatusCode, reply};
7 8
9use blake2::{Blake2s, Digest};
10
8use std::fs; 11use std::fs;
9 12
10use crate::schema::{AuthRequest, Block, Db, MetuId, Transaction, User}; 13use gradecoin::schema::{AuthRequest, Block, Db, MetuId, NakedBlock, Transaction, User};
11 14
12/// POST /register 15/// POST /register
13/// Enables a student to introduce themselves to the system 16/// Enables a student to introduce themselves to the system
@@ -22,7 +25,6 @@ pub async fn authenticate_user(
22 let userlist = db.users.upgradable_read(); 25 let userlist = db.users.upgradable_read();
23 26
24 if userlist.contains_key(&given_id) { 27 if userlist.contains_key(&given_id) {
25
26 let res = Response::builder() 28 let res = Response::builder()
27 .status(StatusCode::BAD_REQUEST) 29 .status(StatusCode::BAD_REQUEST)
28 .body("This user is already authenticated"); 30 .body("This user is already authenticated");
@@ -124,24 +126,44 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply,
124 } 126 }
125 } 127 }
126 128
127 // TODO: check 2, block hash (\w nonce) asserts $hash_condition? <07-04-21, yigit> // 129 let naked_block = NakedBlock {
128 // assume it is for now 130 transaction_list: new_block.transaction_list.clone(),
131 nonce: new_block.nonce.clone(),
132 timestamp: new_block.timestamp.clone(),
133 };
129 134
130 let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain); 135 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap();
131 136
132 let block_json = serde_json::to_string(&new_block).unwrap(); 137 let hashvalue = Blake2s::digest(&naked_block_flat);
138 let hash_string = format!("{:x}", hashvalue);
133 139
134 // let mut file = File::create(format!("{}.block", new_block.timestamp.timestamp())).unwrap(); 140 // 5 rightmost bits are zero
135 fs::write( 141 let should_zero = hashvalue[31] as i32 + hashvalue[30] as i32 + (hashvalue[29] << 4) as i32;
136 format!("blocks/{}.block", new_block.timestamp.timestamp()),
137 block_json,
138 )
139 .unwrap();
140 142
141 *blockchain = new_block; 143 if should_zero == 0 {
144 // one last check to see if block is telling the truth
145 if hash_string == new_block.hash {
146 let mut blockchain = RwLockUpgradableReadGuard::upgrade(blockchain);
142 147
143 let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions); 148 let block_json = serde_json::to_string(&new_block).unwrap();
144 pending_transactions.clear();
145 149
146 Ok(StatusCode::CREATED) 150 fs::write(
151 format!("blocks/{}.block", new_block.timestamp.timestamp()),
152 block_json,
153 )
154 .unwrap();
155
156 *blockchain = new_block;
157
158 let mut pending_transactions = RwLockUpgradableReadGuard::upgrade(pending_transactions);
159 pending_transactions.clear();
160
161 Ok(StatusCode::CREATED)
162 } else {
163 Ok(StatusCode::BAD_REQUEST)
164 }
165 } else {
166 // reject
167 Ok(StatusCode::BAD_REQUEST)
168 }
147} 169}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..aed4591
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,9 @@
1pub mod schema;
2
3pub use schema::create_database;
4pub use schema::AuthRequest;
5pub use schema::Block;
6pub use schema::Db;
7pub use schema::MetuId;
8pub use schema::Transaction;
9pub use schema::User;
diff --git a/src/main.rs b/src/main.rs
index 373223c..5683aea 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,10 @@
1use gradecoin::schema::create_database;
1use std::env; 2use std::env;
2use warp::Filter; 3use warp::Filter;
3 4
4mod handlers;
5mod custom_filters; 5mod custom_filters;
6mod handlers;
6mod routes; 7mod routes;
7mod schema;
8// mod validators; 8// mod validators;
9 9
10#[tokio::main] 10#[tokio::main]
@@ -15,7 +15,7 @@ async fn main() {
15 } 15 }
16 pretty_env_logger::init(); 16 pretty_env_logger::init();
17 17
18 let db = schema::create_database(); 18 let db = create_database();
19 19
20 let api = routes::consensus_routes(db); 20 let api = routes::consensus_routes(db);
21 21
diff --git a/src/routes.rs b/src/routes.rs
index 9f0adc5..03a2569 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -2,7 +2,7 @@ use warp::{Filter, Rejection, Reply};
2 2
3use crate::custom_filters; 3use crate::custom_filters;
4use crate::handlers; 4use crate::handlers;
5use crate::schema::Db; 5use gradecoin::schema::Db;
6 6
7/// Root, all routes combined 7/// Root, all routes combined
8pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { 8pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
@@ -65,12 +65,11 @@ mod tests {
65 // use std::sync::Arc; 65 // use std::sync::Arc;
66 use warp::http::StatusCode; 66 use warp::http::StatusCode;
67 67
68 use crate::schema; 68 use gradecoin::schema::{create_database, AuthRequest, Block, Transaction};
69 use crate::schema::{AuthRequest, Block, Transaction};
70 69
71 /// Create a mock database to be used in tests 70 /// Create a mock database to be used in tests
72 fn mocked_db() -> Db { 71 fn mocked_db() -> Db {
73 let db = schema::create_database(); 72 let db = create_database();
74 73
75 db.pending_transactions.write().insert( 74 db.pending_transactions.write().insert(
76 "hash_value".to_owned(), 75 "hash_value".to_owned(),
@@ -88,7 +87,7 @@ mod tests {
88 "old_transaction_hash_2".to_owned(), 87 "old_transaction_hash_2".to_owned(),
89 "old_transaction_hash_3".to_owned(), 88 "old_transaction_hash_3".to_owned(),
90 ], 89 ],
91 nonce: "not_a_thing_yet".to_owned(), 90 nonce: 0,
92 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), 91 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
93 hash: "not_a_thing_yet".to_owned(), 92 hash: "not_a_thing_yet".to_owned(),
94 }; 93 };
@@ -122,6 +121,26 @@ mod tests {
122 } 121 }
123 } 122 }
124 123
124 /// Create a mock block with a correct mined hash to be used in tests
125 fn mocked_block() -> Block {
126 Block {
127 transaction_list: vec!["hash_value".to_owned()],
128 nonce: 560108,
129 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
130 hash: "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned(),
131 }
132 }
133
134 /// Create a mock block with a wrong hash and nonce
135 fn mocked_wrong_block() -> Block {
136 Block {
137 transaction_list: vec!["foobarbaz".to_owned(), "dazsaz".to_owned()],
138 nonce: 1000, // can you imagine
139 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 12).and_hms(05, 29, 30),
140 hash: "tnarstnarsuthnarsthlarjstk".to_owned(),
141 }
142 }
143
125 /// Test simple GET request to /transaction, resource that exists 144 /// Test simple GET request to /transaction, resource that exists
126 /// https://tools.ietf.org/html/rfc7231#section-6.3.1 145 /// https://tools.ietf.org/html/rfc7231#section-6.3.1
127 /// We should get the only pending transaction available in the database as json 146 /// We should get the only pending transaction available in the database as json
@@ -160,7 +179,7 @@ mod tests {
160 179
161 assert_eq!(res.status(), StatusCode::OK); 180 assert_eq!(res.status(), StatusCode::OK);
162 181
163 let expected_json_body = r#"{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":"not_a_thing_yet","timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}"#; 182 let expected_json_body = r#"{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":0,"timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}"#;
164 assert_eq!(res.body(), expected_json_body); 183 assert_eq!(res.body(), expected_json_body);
165 } 184 }
166 185
@@ -201,7 +220,48 @@ mod tests {
201 assert_eq!(db.pending_transactions.read().len(), 2); 220 assert_eq!(db.pending_transactions.read().len(), 2);
202 } 221 }
203 222
204 /// TEST a POST request to /transaction, an endpoint that exists 223 /// Test a POST request to /block, a resource that exists
224 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
225 /// Should accept the json request, create
226 /// the block
227 #[tokio::test]
228 async fn post_block_201() {
229 let db = mocked_db();
230 let filter = consensus_routes(db.clone());
231
232 let res = warp::test::request()
233 .method("POST")
234 .json(&mocked_block())
235 .path("/block")
236 .reply(&filter)
237 .await;
238
239 assert_eq!(res.status(), StatusCode::CREATED);
240 assert_eq!(
241 *db.blockchain.read().hash,
242 "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned()
243 );
244 }
245
246 /// Test a POST request to /block, a resource that exists
247 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
248 /// Should reject the block because of the wrong hash
249 #[tokio::test]
250 async fn post_block_wrong_hash() {
251 let db = mocked_db();
252 let filter = consensus_routes(db.clone());
253
254 let res = warp::test::request()
255 .method("POST")
256 .json(&mocked_wrong_block())
257 .path("/block")
258 .reply(&filter)
259 .await;
260
261 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
262 }
263
264 /// Test a POST request to /register, an endpoint that exists
205 /// https://tools.ietf.org/html/rfc7231#section-6.3.2 265 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
206 /// Should accept the json request, create a new user and 266 /// Should accept the json request, create a new user and
207 /// add it to the user hashmap in the db 267 /// add it to the user hashmap in the db
@@ -221,9 +281,10 @@ mod tests {
221 assert_eq!(res.status(), StatusCode::CREATED); 281 assert_eq!(res.status(), StatusCode::CREATED);
222 assert_eq!(db.users.read().len(), 1); 282 assert_eq!(db.users.read().len(), 1);
223 } 283 }
224 /// TEST a POST request to /transaction, an endpoint that exists 284
285 /// Test a POST request to /transaction, an endpoint that exists
225 /// https://tools.ietf.org/html/rfc7231#section-6.3.2 286 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
226 /// Should NOT accept the json request 287 /// Should NOT accept the json request as the user is unpriviliged
227 #[tokio::test] 288 #[tokio::test]
228 async fn post_register_unpriviliged_user() { 289 async fn post_register_unpriviliged_user() {
229 let db = mocked_db(); 290 let db = mocked_db();
@@ -261,6 +322,5 @@ mod tests {
261 } 322 }
262} 323}
263 324
264// TODO: POST block test <09-04-21, yigit> //
265// TODO: POST block without correct transactions test <09-04-21, yigit> // 325// TODO: POST block without correct transactions test <09-04-21, yigit> //
266// TODO: POST transaction while that source has pending transaction test <09-04-21, yigit> // 326// TODO: POST transaction while that source has pending transaction test <09-04-21, yigit> //
diff --git a/src/schema.rs b/src/schema.rs
index 909b5cd..98291d7 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -62,17 +62,25 @@ pub struct Block {
62 // somewhere 62 // somewhere
63 // I want to keep this as a String vector because it makes things easier elsewhere 63 // I want to keep this as a String vector because it makes things easier elsewhere
64 pub transaction_list: Vec<String>, // hashes of the transactions (or just "source" for now) 64 pub transaction_list: Vec<String>, // hashes of the transactions (or just "source" for now)
65 pub nonce: String, 65 pub nonce: u32,
66 pub timestamp: NaiveDateTime, 66 pub timestamp: NaiveDateTime,
67 pub hash: String, // future proof'd baby 67 pub hash: String, // future proof'd baby
68} 68}
69 69
70/// For prototyping and letting serde handle everything json
71#[derive(Serialize, Deserialize, Debug)]
72pub struct NakedBlock {
73 pub transaction_list: Vec<String>,
74 pub nonce: u32,
75 pub timestamp: NaiveDateTime,
76}
77
70impl Block { 78impl Block {
71 /// Genesis block 79 /// Genesis block
72 pub fn new() -> Block { 80 pub fn new() -> Block {
73 Block { 81 Block {
74 transaction_list: vec![], 82 transaction_list: vec![],
75 nonce: String::from(""), 83 nonce: 0,
76 timestamp: NaiveDate::from_ymd(2021, 04, 11).and_hms(20, 45, 00), 84 timestamp: NaiveDate::from_ymd(2021, 04, 11).and_hms(20, 45, 00),
77 hash: String::from(""), 85 hash: String::from(""),
78 } 86 }