├── rust-toolchain ├── .gitignore ├── README.md ├── Cargo.toml └── src ├── main.rs └── lib.rs /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLOCKCHAIN 2 | 3 | https://hackernoon.com/learn-blockchains-by-building-one-117428612f46 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blockchain" 3 | version = "0.1.0" 4 | authors = ["Mario Idival "] 5 | 6 | [[bin]] 7 | name = "blockchain" 8 | path = "src/main.rs" 9 | 10 | [dependencies] 11 | serde = "1.0" 12 | serde_json = "1.0" 13 | serde_derive = "1.0" 14 | chrono = { version = "0.4", features = ["serde"] } 15 | rust-crypto = "^0.2" 16 | url = "1.6.0" 17 | hyper = "0.10.0" 18 | 19 | rocket = { git = "https://github.com/SergioBenitez/Rocket" } 20 | #rocket_codegen = "0.3.3" 21 | rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", default-features = false, features = ["json"] } 22 | lazy_static = "0.2" 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // #![feature(plugin)] 2 | // #![plugin(rocket_codegen)] 3 | #![feature(decl_macro)] 4 | #![feature(proc_macro_hygiene)] 5 | 6 | // extern crate rocket; 7 | extern crate serde; 8 | extern crate serde_json; 9 | extern crate blockchain; 10 | 11 | #[macro_use] 12 | extern crate rocket; 13 | #[macro_use] 14 | extern crate lazy_static; 15 | #[macro_use] 16 | extern crate rocket_contrib; 17 | #[macro_use] 18 | extern crate serde_derive; 19 | 20 | use std::sync::Mutex; 21 | // use rocket_contrib::{Json, Value}; 22 | use rocket_contrib::json::{Json, JsonValue}; 23 | use blockchain::*; 24 | 25 | lazy_static! { 26 | static ref GLOBAL_BLOCKCHAIN: Mutex = Mutex::new(Blockchain::new()); 27 | } 28 | 29 | #[get("/mine")] 30 | fn mine() -> Json { 31 | let mut blockchain = GLOBAL_BLOCKCHAIN.lock().unwrap(); 32 | 33 | let chain = blockchain.chain.to_vec(); 34 | let last_block = chain.last().unwrap(); 35 | let last_proof: i64 = last_block.proof; 36 | let proof = blockchain.proof_of_work(last_proof); 37 | 38 | blockchain.new_transaction( 39 | String::from("0"), 40 | String::from("57e430de001d498fbf6e493a79665d57"), 41 | 1.0, 42 | ); 43 | let block = blockchain.new_block(proof, String::new()); 44 | 45 | Json(json!({ 46 | "message": "new block forged", 47 | "index": block.index, 48 | "transactions": block.transactions, 49 | "proof": block.proof, 50 | "previous_hash": block.previous_hash, 51 | })) 52 | } 53 | 54 | #[get("/chain")] 55 | fn chain() -> Json { 56 | let blockchain = GLOBAL_BLOCKCHAIN.lock().unwrap(); 57 | 58 | Json( 59 | json!({"chain": blockchain.chain, "length": blockchain.chain.len()}), 60 | ) 61 | } 62 | 63 | #[get("/nodes/resolve")] 64 | fn nodes_resolve() -> Json { 65 | let mut blockchain = GLOBAL_BLOCKCHAIN.lock().unwrap(); 66 | 67 | let message = if blockchain.resolve_conflicts() { 68 | "Our chain was replaced" 69 | } else { 70 | "Our chain is authoritative" 71 | }; 72 | Json(json!({"message": message, "chain": blockchain.chain})) 73 | } 74 | 75 | #[post("/nodes/register", format = "application/json", data = "")] 76 | fn nodes_register(nodes: Json) -> Json { 77 | if nodes.address.len() <= 0 { 78 | return Json(json!({"error": "send some address"})); 79 | } 80 | let mut blockchain = GLOBAL_BLOCKCHAIN.lock().unwrap(); 81 | 82 | for node in nodes.address.iter() { 83 | blockchain.register_nodes(node.clone()) 84 | } 85 | 86 | Json( 87 | json!({"message": "New nodes have been added", "total_nodes": blockchain.chain.len()}), 88 | ) 89 | } 90 | 91 | #[post("/transaction/new", format = "application/json", data = "")] 92 | fn transactions(transaction: Json) -> Json { 93 | let mut blockchain = GLOBAL_BLOCKCHAIN.lock().unwrap(); 94 | let index = blockchain.new_transaction( 95 | transaction.sender.clone(), 96 | transaction.recipient.clone(), 97 | transaction.amount, 98 | ); 99 | Json( 100 | json!({"message": format!("new transaction created, index {}", index)}), 101 | ) 102 | } 103 | 104 | fn main() { 105 | rocket::ignite() 106 | .mount( 107 | "/", 108 | routes![ 109 | mine, 110 | chain, 111 | nodes_resolve, 112 | nodes_register, 113 | transactions, 114 | ], 115 | ) 116 | .launch(); 117 | } 118 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | extern crate serde_json; 3 | extern crate chrono; 4 | extern crate crypto; 5 | extern crate url; 6 | extern crate hyper; 7 | 8 | use std::io::Read; 9 | use std::collections::HashSet; 10 | use url::Url; 11 | use chrono::prelude::*; 12 | use crypto::sha2::Sha256; 13 | use crypto::digest::Digest; 14 | use hyper::Client; 15 | 16 | #[macro_use] 17 | extern crate serde_derive; 18 | 19 | #[derive(Debug, Clone, Serialize, Deserialize)] 20 | pub struct Transaction { 21 | pub sender: String, 22 | pub recipient: String, 23 | pub amount: f64, 24 | } 25 | 26 | #[derive(Debug, Clone, Serialize, Deserialize)] 27 | pub struct Block { 28 | pub index: i64, 29 | pub timestamp: i64, 30 | pub transactions: Vec, 31 | pub proof: i64, 32 | pub previous_hash: String, 33 | } 34 | 35 | 36 | #[derive(Debug, Serialize, Deserialize)] 37 | pub struct Nodes { 38 | pub address: Vec, 39 | } 40 | 41 | #[derive(Serialize, Debug)] 42 | pub struct Blockchain { 43 | pub chain: Vec, 44 | pub current_transactions: Vec, 45 | pub nodes: HashSet, 46 | } 47 | 48 | #[derive(Deserialize, Debug)] 49 | struct ChainResponse { 50 | length: i64, 51 | chain: Vec, 52 | } 53 | 54 | 55 | impl Blockchain { 56 | pub fn new() -> Blockchain { 57 | let mut blockchain = Blockchain { 58 | chain: Vec::new(), 59 | current_transactions: Vec::new(), 60 | nodes: HashSet::new(), 61 | }; 62 | 63 | // genesis block 64 | blockchain.new_block(100, String::from("1")); 65 | blockchain 66 | } 67 | 68 | pub fn new_transaction(&mut self, sender: String, recipient: String, amount: f64) -> i64 { 69 | self.current_transactions.push(Transaction { 70 | sender, 71 | recipient, 72 | amount, 73 | }); 74 | 75 | self.chain.last().unwrap().index + 1 76 | } 77 | 78 | pub fn new_block(&mut self, proof: i64, previous_hash: String) -> Block { 79 | let utc: DateTime = Utc::now(); 80 | let phash = if !previous_hash.is_empty() { 81 | previous_hash 82 | } else { 83 | Blockchain::hash(&self.chain.last().unwrap()) 84 | }; 85 | 86 | let nblock = &Block { 87 | index: (self.chain.len() + 1) as i64, 88 | timestamp: utc.timestamp(), 89 | transactions: self.current_transactions.to_vec(), 90 | previous_hash: phash, 91 | proof, 92 | }; 93 | 94 | self.current_transactions = Vec::new(); 95 | self.chain.push(nblock.clone()); 96 | nblock.clone() 97 | } 98 | 99 | pub fn hash(block: &Block) -> String { 100 | let mut hasher = Sha256::new(); 101 | let block_string = serde_json::to_string(block).unwrap(); 102 | hasher.input_str(&block_string); 103 | 104 | hasher.result_str() 105 | } 106 | 107 | pub fn proof_of_work(&self, last_proof: i64) -> i64 { 108 | let mut proof = 0i64; 109 | while Blockchain::valid_proof(last_proof, proof) == false { 110 | proof = proof + 1; 111 | } 112 | proof 113 | } 114 | 115 | fn valid_proof(last_proof: i64, proof: i64) -> bool { 116 | let mut hasher = Sha256::new(); 117 | let guess = &format!("{}{}", last_proof, proof); 118 | hasher.input_str(guess); 119 | let output = hasher.result_str(); 120 | &output[..4] == "0000" 121 | } 122 | 123 | pub fn register_nodes(&mut self, address: String) { 124 | let url = Url::parse(&address).unwrap(); 125 | let host_port = format!("{}:{}", url.host_str().unwrap(), url.port().unwrap()); 126 | self.nodes.insert(host_port); 127 | } 128 | 129 | fn valid_chain(chain: &Vec) -> bool { 130 | let mut last_block = chain.first().unwrap(); 131 | let mut current_index = 1; 132 | 133 | while current_index < chain.len() { 134 | let block = &chain[current_index]; 135 | println!("[last block] {:?}", last_block); 136 | println!("[current block] {:?}", block); 137 | 138 | if block.previous_hash != Blockchain::hash(last_block) { 139 | return false; 140 | } 141 | 142 | if !Blockchain::valid_proof(last_block.proof, block.proof) { 143 | return false; 144 | } 145 | 146 | last_block = █ 147 | current_index = current_index + 1; 148 | } 149 | 150 | true 151 | } 152 | 153 | pub fn resolve_conflicts(&mut self) -> bool { 154 | let mut max_length: i64 = self.chain.len() as i64; 155 | let mut new_chain: Vec = Vec::new(); 156 | 157 | for node in self.nodes.iter() { 158 | let url = format!("http://{}/chain", node); 159 | let buf_content = get_content(&url).unwrap(); 160 | let content: ChainResponse = serde_json::from_str(&buf_content).unwrap(); 161 | 162 | if content.length > max_length && Blockchain::valid_chain(&content.chain) { 163 | max_length = content.length; 164 | new_chain = content.chain.clone(); 165 | } 166 | } 167 | 168 | if new_chain.len() > 0 { 169 | self.chain = new_chain.clone(); 170 | return true; 171 | } 172 | false 173 | } 174 | } 175 | 176 | 177 | fn get_content(url: &str) -> hyper::Result { 178 | let client = Client::new(); 179 | let mut response = client.get(url).send()?; 180 | let mut buf = String::new(); 181 | response.read_to_string(&mut buf)?; 182 | Ok(buf) 183 | } 184 | 185 | 186 | #[test] 187 | fn it_works() { 188 | let mut blockchain = Blockchain::new(); 189 | blockchain.register_nodes(String::from("http://localhost:8000")); 190 | println!("{:?}", blockchain.nodes); 191 | println!("{:?}", blockchain.resolve_conflicts()); 192 | } 193 | --------------------------------------------------------------------------------