summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock178
-rw-r--r--Cargo.toml2
-rw-r--r--TODO.md12
-rw-r--r--examples/mining.rs4
-rw-r--r--src/custom_filters.rs4
-rw-r--r--src/handlers.rs60
-rw-r--r--src/routes.rs132
-rw-r--r--src/schema.rs14
8 files changed, 374 insertions, 32 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1116c65..9b91b30 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -75,6 +75,12 @@ dependencies = [
75] 75]
76 76
77[[package]] 77[[package]]
78name = "bumpalo"
79version = "3.6.1"
80source = "registry+https://github.com/rust-lang/crates.io-index"
81checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
82
83[[package]]
78name = "byteorder" 84name = "byteorder"
79version = "1.4.3" 85version = "1.4.3"
80source = "registry+https://github.com/rust-lang/crates.io-index" 86source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -93,6 +99,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
93checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 99checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
94 100
95[[package]] 101[[package]]
102name = "cc"
103version = "1.0.67"
104source = "registry+https://github.com/rust-lang/crates.io-index"
105checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
106
107[[package]]
96name = "cfg-if" 108name = "cfg-if"
97version = "0.1.10" 109version = "0.1.10"
98source = "registry+https://github.com/rust-lang/crates.io-index" 110source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -304,8 +316,10 @@ dependencies = [
304 "blake2", 316 "blake2",
305 "chrono", 317 "chrono",
306 "hex-literal", 318 "hex-literal",
319 "jsonwebtoken",
307 "lazy_static", 320 "lazy_static",
308 "log", 321 "log",
322 "md-5",
309 "parking_lot", 323 "parking_lot",
310 "pretty_env_logger", 324 "pretty_env_logger",
311 "serde", 325 "serde",
@@ -493,6 +507,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
493checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 507checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
494 508
495[[package]] 509[[package]]
510name = "js-sys"
511version = "0.3.50"
512source = "registry+https://github.com/rust-lang/crates.io-index"
513checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
514dependencies = [
515 "wasm-bindgen",
516]
517
518[[package]]
519name = "jsonwebtoken"
520version = "7.2.0"
521source = "registry+https://github.com/rust-lang/crates.io-index"
522checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32"
523dependencies = [
524 "base64 0.12.3",
525 "pem",
526 "ring",
527 "serde",
528 "serde_json",
529 "simple_asn1",
530]
531
532[[package]]
496name = "kernel32-sys" 533name = "kernel32-sys"
497version = "0.2.2" 534version = "0.2.2"
498source = "registry+https://github.com/rust-lang/crates.io-index" 535source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -539,6 +576,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
539checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 576checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
540 577
541[[package]] 578[[package]]
579name = "md-5"
580version = "0.9.1"
581source = "registry+https://github.com/rust-lang/crates.io-index"
582checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
583dependencies = [
584 "block-buffer",
585 "digest",
586 "opaque-debug",
587]
588
589[[package]]
542name = "memchr" 590name = "memchr"
543version = "2.3.4" 591version = "2.3.4"
544source = "registry+https://github.com/rust-lang/crates.io-index" 592source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -621,6 +669,17 @@ dependencies = [
621] 669]
622 670
623[[package]] 671[[package]]
672name = "num-bigint"
673version = "0.2.6"
674source = "registry+https://github.com/rust-lang/crates.io-index"
675checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
676dependencies = [
677 "autocfg",
678 "num-integer",
679 "num-traits",
680]
681
682[[package]]
624name = "num-integer" 683name = "num-integer"
625version = "0.1.44" 684version = "0.1.44"
626source = "registry+https://github.com/rust-lang/crates.io-index" 685source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -640,6 +699,12 @@ dependencies = [
640] 699]
641 700
642[[package]] 701[[package]]
702name = "once_cell"
703version = "1.7.2"
704source = "registry+https://github.com/rust-lang/crates.io-index"
705checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
706
707[[package]]
643name = "opaque-debug" 708name = "opaque-debug"
644version = "0.3.0" 709version = "0.3.0"
645source = "registry+https://github.com/rust-lang/crates.io-index" 710source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -670,6 +735,17 @@ dependencies = [
670] 735]
671 736
672[[package]] 737[[package]]
738name = "pem"
739version = "0.8.3"
740source = "registry+https://github.com/rust-lang/crates.io-index"
741checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
742dependencies = [
743 "base64 0.13.0",
744 "once_cell",
745 "regex",
746]
747
748[[package]]
673name = "percent-encoding" 749name = "percent-encoding"
674version = "2.1.0" 750version = "2.1.0"
675source = "registry+https://github.com/rust-lang/crates.io-index" 751source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -897,6 +973,21 @@ dependencies = [
897] 973]
898 974
899[[package]] 975[[package]]
976name = "ring"
977version = "0.16.20"
978source = "registry+https://github.com/rust-lang/crates.io-index"
979checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
980dependencies = [
981 "cc",
982 "libc",
983 "once_cell",
984 "spin",
985 "untrusted",
986 "web-sys",
987 "winapi 0.3.9",
988]
989
990[[package]]
900name = "ryu" 991name = "ryu"
901version = "1.0.5" 992version = "1.0.5"
902source = "registry+https://github.com/rust-lang/crates.io-index" 993source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -986,6 +1077,17 @@ dependencies = [
986] 1077]
987 1078
988[[package]] 1079[[package]]
1080name = "simple_asn1"
1081version = "0.4.1"
1082source = "registry+https://github.com/rust-lang/crates.io-index"
1083checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b"
1084dependencies = [
1085 "chrono",
1086 "num-bigint",
1087 "num-traits",
1088]
1089
1090[[package]]
989name = "slab" 1091name = "slab"
990version = "0.4.2" 1092version = "0.4.2"
991source = "registry+https://github.com/rust-lang/crates.io-index" 1093source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1009,6 +1111,12 @@ dependencies = [
1009] 1111]
1010 1112
1011[[package]] 1113[[package]]
1114name = "spin"
1115version = "0.5.2"
1116source = "registry+https://github.com/rust-lang/crates.io-index"
1117checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
1118
1119[[package]]
1012name = "subtle" 1120name = "subtle"
1013version = "2.4.0" 1121version = "2.4.0"
1014source = "registry+https://github.com/rust-lang/crates.io-index" 1122source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1241,6 +1349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1241checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1349checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
1242 1350
1243[[package]] 1351[[package]]
1352name = "untrusted"
1353version = "0.7.1"
1354source = "registry+https://github.com/rust-lang/crates.io-index"
1355checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
1356
1357[[package]]
1244name = "url" 1358name = "url"
1245version = "2.2.1" 1359version = "2.2.1"
1246source = "registry+https://github.com/rust-lang/crates.io-index" 1360source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1321,6 +1435,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1321checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1435checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
1322 1436
1323[[package]] 1437[[package]]
1438name = "wasm-bindgen"
1439version = "0.2.73"
1440source = "registry+https://github.com/rust-lang/crates.io-index"
1441checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
1442dependencies = [
1443 "cfg-if 1.0.0",
1444 "wasm-bindgen-macro",
1445]
1446
1447[[package]]
1448name = "wasm-bindgen-backend"
1449version = "0.2.73"
1450source = "registry+https://github.com/rust-lang/crates.io-index"
1451checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
1452dependencies = [
1453 "bumpalo",
1454 "lazy_static",
1455 "log",
1456 "proc-macro2",
1457 "quote",
1458 "syn",
1459 "wasm-bindgen-shared",
1460]
1461
1462[[package]]
1463name = "wasm-bindgen-macro"
1464version = "0.2.73"
1465source = "registry+https://github.com/rust-lang/crates.io-index"
1466checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
1467dependencies = [
1468 "quote",
1469 "wasm-bindgen-macro-support",
1470]
1471
1472[[package]]
1473name = "wasm-bindgen-macro-support"
1474version = "0.2.73"
1475source = "registry+https://github.com/rust-lang/crates.io-index"
1476checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
1477dependencies = [
1478 "proc-macro2",
1479 "quote",
1480 "syn",
1481 "wasm-bindgen-backend",
1482 "wasm-bindgen-shared",
1483]
1484
1485[[package]]
1486name = "wasm-bindgen-shared"
1487version = "0.2.73"
1488source = "registry+https://github.com/rust-lang/crates.io-index"
1489checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
1490
1491[[package]]
1492name = "web-sys"
1493version = "0.3.50"
1494source = "registry+https://github.com/rust-lang/crates.io-index"
1495checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
1496dependencies = [
1497 "js-sys",
1498 "wasm-bindgen",
1499]
1500
1501[[package]]
1324name = "winapi" 1502name = "winapi"
1325version = "0.2.8" 1503version = "0.2.8"
1326source = "registry+https://github.com/rust-lang/crates.io-index" 1504source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 7e12c2d..6701daf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,8 @@ serde_json = "1.0.59"
18lazy_static = "1.4.0" 18lazy_static = "1.4.0"
19blake2 = "0.9.1" 19blake2 = "0.9.1"
20hex-literal = "0.3.1" 20hex-literal = "0.3.1"
21jsonwebtoken = "7.2.0"
22md-5 = "0.9.1"
21 23
22[dev-dependencies] 24[dev-dependencies]
23serde_test = "1.0.117" 25serde_test = "1.0.117"
diff --git a/TODO.md b/TODO.md
index b311777..622c95a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,14 +3,7 @@
3## Proof-of-work 3## Proof-of-work
4So far so good 4So far so good
5 5
6## Authentication 6## Authentication(DONE/DONE)
7- [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect
8- [ ] implement JWT
9 - https://blog.logrocket.com/jwt-authentication-in-rust/
10 - https://crates.io/crates/jsonwebtoken
11 - https://jwt.io/introduction/
12 - https://jwt.io/#debugger-io
13- [ ] users should be able to _sign_ their transactions
14 7
15## Verbosity 8## Verbosity
16- [ ] Verbose error messages (use error.rs?) 9- [ ] Verbose error messages (use error.rs?)
@@ -31,3 +24,6 @@ So far so good
31- [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05) 24- [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05)
32- [x] pick a block proposal scheme (= pick hash function) [list of hash functions](https://en.bitcoinwiki.org/wiki/List_of_hash_functions) (done at 2021-04-12 05:30) 25- [x] pick a block proposal scheme (= pick hash function) [list of hash functions](https://en.bitcoinwiki.org/wiki/List_of_hash_functions) (done at 2021-04-12 05:30)
33- [x] check the nonce for incoming blocks (done at 2021-04-12 05:30) 26- [x] check the nonce for incoming blocks (done at 2021-04-12 05:30)
27- [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect
28- [X] implement JWT
29- [X] users should be able to _sign_ their transactions
diff --git a/examples/mining.rs b/examples/mining.rs
index c95c214..21fdc58 100644
--- a/examples/mining.rs
+++ b/examples/mining.rs
@@ -7,9 +7,7 @@ use blake2::{Blake2s, Digest};
7 7
8pub fn main() { 8pub fn main() {
9 let mut b = NakedBlock { 9 let mut b = NakedBlock {
10 transaction_list: vec![ 10 transaction_list: vec!["hash_value".to_owned()],
11 "hash_value".to_owned(),
12 ],
13 nonce: 0, 11 nonce: 0,
14 timestamp: NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), 12 timestamp: NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
15 }; 13 };
diff --git a/src/custom_filters.rs b/src/custom_filters.rs
index 315ba4a..f93f572 100644
--- a/src/custom_filters.rs
+++ b/src/custom_filters.rs
@@ -20,6 +20,10 @@ pub fn transaction_json_body() -> impl Filter<Extract = (Transaction,), Error =
20 warp::body::content_length_limit(1024 * 32).and(warp::body::json()) 20 warp::body::content_length_limit(1024 * 32).and(warp::body::json())
21} 21}
22 22
23pub fn auth_header() -> impl Filter<Extract = (String,), Error = Rejection> + Clone {
24 warp::header::header::<String>("Authorization")
25}
26
23// Accept only json encoded Block body and reject big payloads 27// Accept only json encoded Block body and reject big payloads
24// TODO: find a good limit for this <11-04-21, yigit> // 28// TODO: find a good limit for this <11-04-21, yigit> //
25pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone { 29pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone {
diff --git a/src/handlers.rs b/src/handlers.rs
index 38bd459..07986f5 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -1,16 +1,29 @@
1use blake2::{Blake2s, Digest};
2use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
1/// API handlers, the ends of each filter chain 3/// API handlers, the ends of each filter chain
2use log::debug; 4use log::debug;
5use md5::Md5;
3use parking_lot::RwLockUpgradableReadGuard; 6use parking_lot::RwLockUpgradableReadGuard;
7use serde::{Deserialize, Serialize};
4use serde_json; 8use serde_json;
5use serde_json::json; 9use serde_json::json;
6use std::convert::Infallible; 10use std::convert::Infallible;
7use warp::{http::Response, http::StatusCode, reply}; 11use std::fs;
12use warp::{http::Response, http::StatusCode, reject, reply};
8 13
9use blake2::{Blake2s, Digest}; 14use gradecoin::schema::{
15 AuthRequest, Block, Db, MetuId, NakedBlock, PublicKeySignature, Transaction, User,
16};
10 17
11use std::fs; 18const BEARER: &str = "Bearer ";
12 19
13use gradecoin::schema::{AuthRequest, Block, Db, MetuId, NakedBlock, Transaction, User}; 20/// tha: Transaction Hash, String
21/// iat: Issued At, Unix Time, epoch
22#[derive(Debug, Serialize, Deserialize)]
23pub struct Claims {
24 pub tha: String,
25 pub iat: usize,
26}
14 27
15/// POST /register 28/// POST /register
16/// Enables a student to introduce themselves to the system 29/// Enables a student to introduce themselves to the system
@@ -167,3 +180,42 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply,
167 Ok(StatusCode::BAD_REQUEST) 180 Ok(StatusCode::BAD_REQUEST)
168 } 181 }
169} 182}
183
184pub async fn auth_propose_transaction(
185 new_transaction: Transaction,
186 token: String,
187 db: Db,
188) -> Result<impl warp::Reply, warp::Rejection> {
189 debug!("new transaction request {:?}", new_transaction);
190 let raw_jwt = token.trim_start_matches(BEARER).to_owned();
191
192 let decoded = jsonwebtoken::decode::<Claims>(
193 &token,
194 &DecodingKey::from_rsa_pem(
195 db.users
196 .read()
197 .get(&new_transaction.by)
198 .unwrap()
199 .public_key
200 .as_bytes(),
201 )
202 .unwrap(),
203 // todo@keles: If user is not found return user not found error
204 &Validation::new(Algorithm::PS256),
205 )
206 .unwrap();
207 // todo: If user is found but header is not validated, return header not valid
208
209 let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap());
210
211 // let mut transactions = db.lock().await;
212 if decoded.claims.tha == format!("{:x}", hashed_transaction) {
213 let mut transactions = db.pending_transactions.write();
214
215 transactions.insert(new_transaction.source.to_owned(), new_transaction);
216
217 Ok(StatusCode::CREATED)
218 } else {
219 Ok(StatusCode::BAD_REQUEST)
220 }
221}
diff --git a/src/routes.rs b/src/routes.rs
index 03a2569..ed2acad 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -1,14 +1,14 @@
1use gradecoin::schema::Db;
1use warp::{Filter, Rejection, Reply}; 2use warp::{Filter, Rejection, Reply};
2 3
3use crate::custom_filters; 4use crate::custom_filters;
4use crate::handlers; 5use crate::handlers;
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 {
9 transaction_list(db.clone()) 9 transaction_list(db.clone())
10 .or(register_user(db.clone())) 10 .or(register_user(db.clone()))
11 .or(transaction_propose(db.clone())) 11 .or(auth_transaction_propose(db.clone()))
12 .or(block_propose(db.clone())) 12 .or(block_propose(db.clone()))
13 .or(block_list(db.clone())) 13 .or(block_list(db.clone()))
14} 14}
@@ -47,6 +47,18 @@ pub fn transaction_propose(db: Db) -> impl Filter<Extract = impl Reply, Error =
47 .and_then(handlers::propose_transaction) 47 .and_then(handlers::propose_transaction)
48} 48}
49 49
50/// POST /transaction warp route
51pub fn auth_transaction_propose(
52 db: Db,
53) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
54 warp::path!("transaction")
55 .and(warp::post())
56 .and(custom_filters::transaction_json_body())
57 .and(custom_filters::auth_header())
58 .and(custom_filters::with_db(db))
59 .and_then(handlers::auth_propose_transaction)
60}
61
50/// POST /block warp route 62/// POST /block warp route
51pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { 63pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
52 warp::path!("block") 64 warp::path!("block")
@@ -58,22 +70,69 @@ pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Reject
58 70
59#[cfg(test)] 71#[cfg(test)]
60mod tests { 72mod tests {
61 use super::*; 73 use gradecoin::schema::{create_database, AuthRequest, Block, MetuId, Transaction, User};
62 74 use handlers::Claims;
63 // use chrono::prelude::*; 75 // use chrono::prelude::*;
64 // use parking_lot::RwLock; 76 // use parking_lot::RwLock;
65 // use std::sync::Arc; 77 // use std::sync::Arc;
66 use warp::http::StatusCode; 78 use warp::http::StatusCode;
67 79
68 use gradecoin::schema::{create_database, AuthRequest, Block, Transaction}; 80 use super::*;
69 81
82 use jsonwebtoken::{Header, encode, EncodingKey, Algorithm};
83 const private_key_pem: &str = "-----BEGIN RSA PRIVATE KEY-----
84MIIEpAIBAAKCAQEA4nU0G4WjkmcQUx0hq6LQuV5Q+ACmUFL/OjoYMDwC/O/6pCd1
85UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBinyrUpnY4mhy0SQUwoeCw7YkcHAyhCj
86NT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1ptSuhFgQxziItamn8maoJ6JUSVEX
87VO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2nEbFdTHS6pHqtZNHQndTmEKwRfh0R
88YtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIGGHwdFxy1niLkXwtHNjV7lnIOkTbx
896+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138sQIDAQABAoIBAD23nYTmrganag6M
90wPFrBSGP79c3Lhx0EjUHQjJbGKFgsdltG48qM3ut+DF9ACy0Z+/7bbC7+39vaIOq
911jLR2d6aiYTaLKseO4s2FawD1sgamvU3BZPsXn0gAhnnU5Gyy8Nas1dccvhoc9wI
92neaZUPrvucQ90AzLfo6r9yacDbYHB1lOyomApUvpJxOgHISGEtc9qGPDrdH19aF0
938fCv2bbQRh+TChgN3IB0o5w0wXaI7YAyAouAv/AzHCoEMpt7OGjFTkjh/ujlPL9O
94+FLuJNsQRHDN0gJo2pcvwGwDCsioMixQ9bZ7ZrUu2BNpEQygyeSbj9ZI1iRvhosO
95JU3rwEECgYEA9MppTYA6A9WQbCCwPH1QMpUAmPNVSWVhUVag4lGOEhdCDRcz9ook
96DohQMKctiEB1luKuvDokxo0uMOfMO9/YwjsRB7qjQip7Th1zMJIjD+A+juLzHK4r
97/RiRtWYGAnF8mptDvE+93JsPb3C/lQLvIhio5GQYWBqPJu6SpeosIskCgYEA7NPi
98Gbffzr2UQhW8BNKmctEEh8yFRVojFo3wwwWxSNUVXGSmSm31CL+Q8h817R+2OkPV
991ZMUOBU4UJiqFt28kIvTDFqbAJlJQGCpY2mY7OLQiD2A+TVLcFrHmoCaPfCAK1Qd
100hQ0PmFK7Mf8qClpA3E5chop/WfKQfiu46sZv1qkCgYAhGdXPcw1lQ1W6KVlrdI6J
101qHhiNlVMDXdxZkNvFxQdAiQeXQrbxaZGiMw/J/wSNpUwCAsUzM/4QVMDrfSCDCzl
102ZtNQtj4pTlFKKNVQthIjrXEIJUw2jp7IJLBfVSJu5iWxSlmId0f3MsiNizN81N69
103P5Rm/doE3+KHoy8VXGsHcQKBgQCkNh62enqjHWypjex6450qS6f6iWN3PRLLVsw0
104TcQpniZblCaBwVCAKmRUnjOEIdL2/4ZLutnwMTaFG/YEOOfAylMiY8jKV38lNmD9
105X4D78CFr9klxgvS2CRwSE03f2NzmLkLxuKaxldvaxPTfjMkgeO1LFMlNExYBhkuH
1067uQpUQKBgQCKX6qMNh2gSdgG7qyxfTFZ4y5EGOBoKe/dE+IcVF3Vnh6DZVbCAbBL
1075EdFWZSrCnDjA4xiKW55mwp95Ud9EZsZAb13L8V9t82eK+UDBoWlb7VRNYpda/x1
1085/i4qQJ28x2UNJDStpYFpnp4Ba1lvXjKngIbDPkjU+hbBJ+BNGAIeg==
109-----END RSA PRIVATE KEY-----";
70 /// Create a mock database to be used in tests 110 /// Create a mock database to be used in tests
71 fn mocked_db() -> Db { 111 fn mocked_db() -> Db {
72 let db = create_database(); 112 let db = create_database();
73 113
114 db.users.write().insert(
115 "mock_transaction_source".to_owned(),
116 User {
117 user_id: MetuId::new("e254275".to_owned()).unwrap(),
118 public_key:
119"-----BEGIN PUBLIC KEY-----
120MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4nU0G4WjkmcQUx0hq6LQ
121uV5Q+ACmUFL/OjoYMDwC/O/6pCd1UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBiny
122rUpnY4mhy0SQUwoeCw7YkcHAyhCjNT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1
123ptSuhFgQxziItamn8maoJ6JUSVEXVO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2n
124EbFdTHS6pHqtZNHQndTmEKwRfh0RYtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIG
125GHwdFxy1niLkXwtHNjV7lnIOkTbx6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138
126sQIDAQAB
127-----END PUBLIC KEY-----"
128 .to_owned(),
129 balance: 0,
130 },
131 );
74 db.pending_transactions.write().insert( 132 db.pending_transactions.write().insert(
75 "hash_value".to_owned(), 133 "hash_value".to_owned(),
76 Transaction { 134 Transaction {
135 by: "source_account".to_owned(),
77 source: "source_account".to_owned(), 136 source: "source_account".to_owned(),
78 target: "target_account".to_owned(), 137 target: "target_account".to_owned(),
79 amount: 20, 138 amount: 20,
@@ -95,6 +154,15 @@ mod tests {
95 db 154 db
96 } 155 }
97 156
157 fn mocked_jwt() -> String {
158
159 let claims = Claims {
160 tha: "6692e774eba7fb92dc0fe6cf7347591e".to_owned(),
161 iat: 1516239022,
162 };
163 let header = Header::new(Algorithm::RS256);
164 encode(&header, &claims, &EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap()).unwrap()
165 }
98 /// Create a mock user that is allowed to be in gradecoin to be used in tests 166 /// Create a mock user that is allowed to be in gradecoin to be used in tests
99 fn priviliged_mocked_user() -> AuthRequest { 167 fn priviliged_mocked_user() -> AuthRequest {
100 AuthRequest { 168 AuthRequest {
@@ -114,6 +182,7 @@ mod tests {
114 /// Create a mock transaction to be used in tests 182 /// Create a mock transaction to be used in tests
115 fn mocked_transaction() -> Transaction { 183 fn mocked_transaction() -> Transaction {
116 Transaction { 184 Transaction {
185 by: "mock_transaction_source".to_owned(),
117 source: "mock_transaction_source".to_owned(), 186 source: "mock_transaction_source".to_owned(),
118 target: "mock_transaction_target".to_owned(), 187 target: "mock_transaction_target".to_owned(),
119 amount: 25, 188 amount: 25,
@@ -125,9 +194,9 @@ mod tests {
125 fn mocked_block() -> Block { 194 fn mocked_block() -> Block {
126 Block { 195 Block {
127 transaction_list: vec!["hash_value".to_owned()], 196 transaction_list: vec!["hash_value".to_owned()],
128 nonce: 560108, 197 nonce: 3831993,
129 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), 198 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
130 hash: "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned(), 199 hash: "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned(),
131 } 200 }
132 } 201 }
133 202
@@ -158,7 +227,7 @@ mod tests {
158 227
159 assert_eq!(res.status(), StatusCode::OK); 228 assert_eq!(res.status(), StatusCode::OK);
160 229
161 let expected_json_body = r#"[{"source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#; 230 let expected_json_body = r#"[{"by":"source_account","source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#;
162 231
163 assert_eq!(res.body(), expected_json_body); 232 assert_eq!(res.body(), expected_json_body);
164 } 233 }
@@ -208,10 +277,30 @@ mod tests {
208 async fn post_json_201() { 277 async fn post_json_201() {
209 let db = mocked_db(); 278 let db = mocked_db();
210 let filter = consensus_routes(db.clone()); 279 let filter = consensus_routes(db.clone());
280 let res = warp::test::request()
281 .method("POST")
282 .json(&mocked_transaction())
283 .path("/transaction")
284 .reply(&filter)
285 .await;
286
287 assert_eq!(res.status(), StatusCode::CREATED);
288 assert_eq!(db.pending_transactions.read().len(), 2);
289 }
290
291 /// Test a POST request to /transaction, a resource that exists
292 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
293 /// Should accept the json request, create
294 /// the transaction and add it to pending transactions in the db
295 #[tokio::test]
296 async fn post_auth_json_201() {
297 let db = mocked_db();
298 let filter = consensus_routes(db.clone());
211 299
212 let res = warp::test::request() 300 let res = warp::test::request()
213 .method("POST") 301 .method("POST")
214 .json(&mocked_transaction()) 302 .json(&mocked_transaction())
303 .header("Authorization", format!("Bearer {}", &mocked_jwt()))
215 .path("/transaction") 304 .path("/transaction")
216 .reply(&filter) 305 .reply(&filter)
217 .await; 306 .await;
@@ -220,6 +309,27 @@ mod tests {
220 assert_eq!(db.pending_transactions.read().len(), 2); 309 assert_eq!(db.pending_transactions.read().len(), 2);
221 } 310 }
222 311
312 /// Test a POST request to /transaction, a resource that exists
313 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
314 /// Should accept the json request, create
315 /// the transaction and add it to pending transactions in the db
316 #[tokio::test]
317 async fn post_auth_json_400() {
318 let db = mocked_db();
319 let filter = consensus_routes(db.clone());
320
321 let res = warp::test::request()
322 .method("POST")
323 .json(&mocked_transaction())
324 .header("Authorization", "Bearer aaaaaaaasdlkjaldkasljdaskjlaaaaaaaaaaaaaa")
325 .path("/transaction")
326 .reply(&filter)
327 .await;
328
329 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
330 assert_eq!(db.pending_transactions.read().len(), 1);
331 }
332
223 /// Test a POST request to /block, a resource that exists 333 /// Test a POST request to /block, a resource that exists
224 /// https://tools.ietf.org/html/rfc7231#section-6.3.2 334 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
225 /// Should accept the json request, create 335 /// Should accept the json request, create
@@ -239,7 +349,7 @@ mod tests {
239 assert_eq!(res.status(), StatusCode::CREATED); 349 assert_eq!(res.status(), StatusCode::CREATED);
240 assert_eq!( 350 assert_eq!(
241 *db.blockchain.read().hash, 351 *db.blockchain.read().hash,
242 "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned() 352 "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned()
243 ); 353 );
244 } 354 }
245 355
@@ -279,7 +389,7 @@ mod tests {
279 389
280 println!("{:?}", res.body()); 390 println!("{:?}", res.body());
281 assert_eq!(res.status(), StatusCode::CREATED); 391 assert_eq!(res.status(), StatusCode::CREATED);
282 assert_eq!(db.users.read().len(), 1); 392 assert_eq!(db.users.read().len(), 2);
283 } 393 }
284 394
285 /// Test a POST request to /transaction, an endpoint that exists 395 /// Test a POST request to /transaction, an endpoint that exists
@@ -299,7 +409,7 @@ mod tests {
299 409
300 println!("{:?}", res.body()); 410 println!("{:?}", res.body());
301 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 411 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
302 assert_eq!(db.users.read().len(), 0); 412 assert_eq!(db.users.read().len(), 1);
303 } 413 }
304 414
305 /// Test a POST request to /transaction, a resource that exists with a longer than expected 415 /// Test a POST request to /transaction, a resource that exists with a longer than expected
diff --git a/src/schema.rs b/src/schema.rs
index 98291d7..39921b8 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -41,18 +41,20 @@ impl Db {
41 } 41 }
42} 42}
43 43
44pub type PublicKeySignature = String;
45
44/// A transaction between `source` and `target` that moves `amount` 46/// A transaction between `source` and `target` that moves `amount`
45#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 47#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
46pub struct Transaction { 48pub struct Transaction {
47 // TODO: new field by <11-04-21, yigit> // 49 pub by: PublicKeySignature,
48 pub source: String, 50 pub source: PublicKeySignature,
49 pub target: String, 51 pub target: PublicKeySignature,
50 pub amount: i32, 52 pub amount: i32,
51 pub timestamp: NaiveDateTime, 53 pub timestamp: NaiveDateTime,
52} 54}
53 55
54/// A block that was proposed with `transaction_list` and `nonce` that made `hash` valid 56/// A block that was proposed with `transaction_list` and `nonce` that made `hash` valid
55/// https://serde.rs/container-attrs.html might be valueable to normalize the serialize/deserialize 57/// https://serde.rs/container-attrs.html might be valuable to normalize the serialize/deserialize
56/// conventions as these will be hashed 58/// conventions as these will be hashed
57#[derive(Serialize, Deserialize, Debug)] 59#[derive(Serialize, Deserialize, Debug)]
58pub struct Block { 60pub struct Block {
@@ -61,7 +63,7 @@ pub struct Block {
61 // we can leave this as is and whenever we have a new block we _could_ just log it to file 63 // we can leave this as is and whenever we have a new block we _could_ just log it to file
62 // somewhere 64 // somewhere
63 // I want to keep this as a String vector because it makes things easier elsewhere 65 // 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) 66 pub transaction_list: Vec<PublicKeySignature>, // hashes of the transactions (or just "source" for now)
65 pub nonce: u32, 67 pub nonce: u32,
66 pub timestamp: NaiveDateTime, 68 pub timestamp: NaiveDateTime,
67 pub hash: String, // future proof'd baby 69 pub hash: String, // future proof'd baby
@@ -70,7 +72,7 @@ pub struct Block {
70/// For prototyping and letting serde handle everything json 72/// For prototyping and letting serde handle everything json
71#[derive(Serialize, Deserialize, Debug)] 73#[derive(Serialize, Deserialize, Debug)]
72pub struct NakedBlock { 74pub struct NakedBlock {
73 pub transaction_list: Vec<String>, 75 pub transaction_list: Vec<PublicKeySignature>,
74 pub nonce: u32, 76 pub nonce: u32,
75 pub timestamp: NaiveDateTime, 77 pub timestamp: NaiveDateTime,
76} 78}