diff options
| author | Yigit Sever | 2021-04-12 05:32:53 +0300 |
|---|---|---|
| committer | Yigit Sever | 2021-04-12 05:32:53 +0300 |
| commit | 44d21b676f90a2fc8b255eb9c1393e53f40c9daa (patch) | |
| tree | edfadfd279dc9fcfaa6c27f819c7f0e69d14599c | |
| parent | 6c0345ecda5e46da88bc6ca513a28c648c29833c (diff) | |
| download | gradecoin-44d21b676f90a2fc8b255eb9c1393e53f40c9daa.tar.gz gradecoin-44d21b676f90a2fc8b255eb9c1393e53f40c9daa.tar.bz2 gradecoin-44d21b676f90a2fc8b255eb9c1393e53f40c9daa.zip | |
Implement proof-of-work
Using blacke2s: https://docs.rs/blake2/0.9.1/blake2/
Using this guy's hash checker https://gist.github.com/gkbrk/2e4835e3a17b3fb6e1e7
blacke2s with 5 bits 0 can mine a block between 20 seconds to 359 during
my tests, hope it'll be fun
| -rw-r--r-- | Cargo.lock | 35 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | TODO.md | 4 | ||||
| -rw-r--r-- | examples/mining.rs | 35 | ||||
| -rw-r--r-- | src/custom_filters.rs | 8 | ||||
| -rw-r--r-- | src/handlers.rs | 54 | ||||
| -rw-r--r-- | src/lib.rs | 9 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/routes.rs | 80 | ||||
| -rw-r--r-- | src/schema.rs | 12 |
10 files changed, 207 insertions, 38 deletions
| @@ -45,6 +45,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 45 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | 45 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" |
| 46 | 46 | ||
| 47 | [[package]] | 47 | [[package]] |
| 48 | name = "blake2" | ||
| 49 | version = "0.9.1" | ||
| 50 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 51 | checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4" | ||
| 52 | dependencies = [ | ||
| 53 | "crypto-mac", | ||
| 54 | "digest", | ||
| 55 | "opaque-debug", | ||
| 56 | ] | ||
| 57 | |||
| 58 | [[package]] | ||
| 48 | name = "block-buffer" | 59 | name = "block-buffer" |
| 49 | version = "0.9.0" | 60 | version = "0.9.0" |
| 50 | source = "registry+https://github.com/rust-lang/crates.io-index" | 61 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -123,6 +134,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 123 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" | 134 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" |
| 124 | 135 | ||
| 125 | [[package]] | 136 | [[package]] |
| 137 | name = "crypto-mac" | ||
| 138 | version = "0.8.0" | ||
| 139 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 140 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" | ||
| 141 | dependencies = [ | ||
| 142 | "generic-array", | ||
| 143 | "subtle", | ||
| 144 | ] | ||
| 145 | |||
| 146 | [[package]] | ||
| 126 | name = "digest" | 147 | name = "digest" |
| 127 | version = "0.9.0" | 148 | version = "0.9.0" |
| 128 | source = "registry+https://github.com/rust-lang/crates.io-index" | 149 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -280,7 +301,9 @@ dependencies = [ | |||
| 280 | name = "gradecoin" | 301 | name = "gradecoin" |
| 281 | version = "0.1.0" | 302 | version = "0.1.0" |
| 282 | dependencies = [ | 303 | dependencies = [ |
| 304 | "blake2", | ||
| 283 | "chrono", | 305 | "chrono", |
| 306 | "hex-literal", | ||
| 284 | "lazy_static", | 307 | "lazy_static", |
| 285 | "log", | 308 | "log", |
| 286 | "parking_lot", | 309 | "parking_lot", |
| @@ -353,6 +376,12 @@ dependencies = [ | |||
| 353 | ] | 376 | ] |
| 354 | 377 | ||
| 355 | [[package]] | 378 | [[package]] |
| 379 | name = "hex-literal" | ||
| 380 | version = "0.3.1" | ||
| 381 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 382 | checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" | ||
| 383 | |||
| 384 | [[package]] | ||
| 356 | name = "http" | 385 | name = "http" |
| 357 | version = "0.2.3" | 386 | version = "0.2.3" |
| 358 | source = "registry+https://github.com/rust-lang/crates.io-index" | 387 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -980,6 +1009,12 @@ dependencies = [ | |||
| 980 | ] | 1009 | ] |
| 981 | 1010 | ||
| 982 | [[package]] | 1011 | [[package]] |
| 1012 | name = "subtle" | ||
| 1013 | version = "2.4.0" | ||
| 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1015 | checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" | ||
| 1016 | |||
| 1017 | [[package]] | ||
| 983 | name = "syn" | 1018 | name = "syn" |
| 984 | version = "1.0.68" | 1019 | version = "1.0.68" |
| 985 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -16,6 +16,8 @@ pretty_env_logger = "0.3.1" | |||
| 16 | parking_lot = "0.10.0" | 16 | parking_lot = "0.10.0" |
| 17 | serde_json = "1.0.59" | 17 | serde_json = "1.0.59" |
| 18 | lazy_static = "1.4.0" | 18 | lazy_static = "1.4.0" |
| 19 | blake2 = "0.9.1" | ||
| 20 | hex-literal = "0.3.1" | ||
| 19 | 21 | ||
| 20 | [dev-dependencies] | 22 | [dev-dependencies] |
| 21 | serde_test = "1.0.117" | 23 | serde_test = "1.0.117" |
| @@ -1,8 +1,6 @@ | |||
| 1 | # TODO | 1 | # TODO |
| 2 | 2 | ||
| 3 | ## Proof-of-work | 3 | ## Proof-of-work |
| 4 | - [ ] pick a block proposal scheme (= pick hash function) [list of hash functions](https://en.bitcoinwiki.org/wiki/List_of_hash_functions) | ||
| 5 | - [ ] check the nonce for incoming blocks | ||
| 6 | 4 | ||
| 7 | ## Authentication | 5 | ## Authentication |
| 8 | - [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect | 6 | - [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect |
| @@ -26,3 +24,5 @@ | |||
| 26 | ## Done & Brag | 24 | ## Done & Brag |
| 27 | - [x] Switch to RwLock (parking_lot) (done at 2021-04-07 03:43, two possible schemes to represent inner Db (ledger) in code) | 25 | - [x] Switch to RwLock (parking_lot) (done at 2021-04-07 03:43, two possible schemes to represent inner Db (ledger) in code) |
| 28 | - [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05) | 26 | - [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05) |
| 27 | - [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) | ||
| 28 | - [x] check the nonce for incoming blocks (done at 2021-04-12 05:30) | ||
diff --git a/examples/mining.rs b/examples/mining.rs new file mode 100644 index 0000000..56e33f3 --- /dev/null +++ b/examples/mining.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | use chrono::NaiveDate; | ||
| 2 | use gradecoin::schema::NakedBlock; | ||
| 3 | use serde_json; | ||
| 4 | use std::time::Instant; | ||
| 5 | |||
| 6 | use blake2::{Blake2s, Digest}; | ||
| 7 | |||
| 8 | pub fn main() { | ||
| 9 | let mut b = NakedBlock { | ||
| 10 | transaction_list: vec![ | ||
| 11 | "hash_value".to_owned(), | ||
| 12 | ], | ||
| 13 | nonce: 0, | ||
| 14 | timestamp: NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | ||
| 15 | }; | ||
| 16 | |||
| 17 | let now = Instant::now(); | ||
| 18 | |||
| 19 | for nonce in 0..u32::MAX { | ||
| 20 | b.nonce = nonce; | ||
| 21 | |||
| 22 | let j = serde_json::to_vec(&b).unwrap(); | ||
| 23 | |||
| 24 | let result = Blake2s::digest(&j); | ||
| 25 | |||
| 26 | let first_five = result[31] as i32 + result[30] as i32 + (result[29] << 4) as i32; | ||
| 27 | |||
| 28 | if first_five == 0 { | ||
| 29 | println!("{} - {:x}\n{:?}", nonce, result, b); | ||
| 30 | break; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | println!("it took {} seconds", now.elapsed().as_secs()); | ||
| 35 | } | ||
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 | 1 | use gradecoin::schema::{AuthRequest, Block, Db, Transaction}; |
| 2 | |||
| 3 | use std::convert::Infallible; | 2 | use std::convert::Infallible; |
| 4 | use warp::{Filter, Rejection}; | 3 | use warp::{Filter, Rejection}; |
| 5 | 4 | ||
| 6 | use crate::schema::{Block, Db, Transaction, AuthRequest}; | ||
| 7 | |||
| 8 | // Database context for routes | 5 | // Database context for routes |
| 9 | pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = Infallible> + Clone { | 6 | pub 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> // |
| 15 | pub fn auth_request_json_body() -> impl Filter<Extract = (AuthRequest,), Error = Rejection> + Clone { | 12 | pub 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 @@ | |||
| 2 | use log::debug; | 2 | use log::debug; |
| 3 | use parking_lot::RwLockUpgradableReadGuard; | 3 | use parking_lot::RwLockUpgradableReadGuard; |
| 4 | use serde_json; | 4 | use serde_json; |
| 5 | use serde_json::json; | ||
| 5 | use std::convert::Infallible; | 6 | use std::convert::Infallible; |
| 6 | use warp::{http::Response, http::StatusCode, reply}; | 7 | use warp::{http::Response, http::StatusCode, reply}; |
| 7 | 8 | ||
| 9 | use blake2::{Blake2s, Digest}; | ||
| 10 | |||
| 8 | use std::fs; | 11 | use std::fs; |
| 9 | 12 | ||
| 10 | use crate::schema::{AuthRequest, Block, Db, MetuId, Transaction, User}; | 13 | use 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 @@ | |||
| 1 | pub mod schema; | ||
| 2 | |||
| 3 | pub use schema::create_database; | ||
| 4 | pub use schema::AuthRequest; | ||
| 5 | pub use schema::Block; | ||
| 6 | pub use schema::Db; | ||
| 7 | pub use schema::MetuId; | ||
| 8 | pub use schema::Transaction; | ||
| 9 | pub 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 @@ | |||
| 1 | use gradecoin::schema::create_database; | ||
| 1 | use std::env; | 2 | use std::env; |
| 2 | use warp::Filter; | 3 | use warp::Filter; |
| 3 | 4 | ||
| 4 | mod handlers; | ||
| 5 | mod custom_filters; | 5 | mod custom_filters; |
| 6 | mod handlers; | ||
| 6 | mod routes; | 7 | mod routes; |
| 7 | mod 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 | ||
| 3 | use crate::custom_filters; | 3 | use crate::custom_filters; |
| 4 | use crate::handlers; | 4 | use crate::handlers; |
| 5 | use crate::schema::Db; | 5 | use gradecoin::schema::Db; |
| 6 | 6 | ||
| 7 | /// Root, all routes combined | 7 | /// Root, all routes combined |
| 8 | pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | 8 | pub 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)] | ||
| 72 | pub struct NakedBlock { | ||
| 73 | pub transaction_list: Vec<String>, | ||
| 74 | pub nonce: u32, | ||
| 75 | pub timestamp: NaiveDateTime, | ||
| 76 | } | ||
| 77 | |||
| 70 | impl Block { | 78 | impl 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 | } |
