├── .gitignore ├── src ├── hashable.rs ├── components │ ├── hashable.rs │ ├── transaction.rs │ ├── main.rs │ ├── block.rs │ ├── lib.rs │ └── blockchain.rs ├── transaction.rs ├── main.rs ├── block.rs ├── lib.rs └── blockchain.rs └── Cargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | 4 | **/*.rs.bk 5 | 6 | Cargo.lock 7 | -------------------------------------------------------------------------------- /src/hashable.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub trait Hashable { 4 | fn bytes (&self) -> Vec; 5 | 6 | fn hash (&self) -> Hash { 7 | crypto_hash::digest(crypto_hash::Algorithm::SHA256, &self.bytes()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/hashable.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub trait Hashable { 4 | fn bytes (&self) -> Vec; 5 | 6 | fn hash (&self) -> Hash { 7 | crypto_hash::digest(crypto_hash::Algorithm::SHA256, &self.bytes()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blockchain-in-rust" 3 | version = "0.1.0" 4 | authors = ["Jacob "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "blockchainlib" 9 | path = "src/lib.rs" 10 | 11 | [[bin]] 12 | name = "blockchain" 13 | path = "src/main.rs" 14 | 15 | [dependencies] 16 | hex = "0.3.2" 17 | crypto-hash = "0.3.3" 18 | -------------------------------------------------------------------------------- /src/transaction.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Clone)] 5 | pub struct Output { 6 | pub to_addr: Address, 7 | pub value: u64, 8 | } 9 | 10 | impl Hashable for Output { 11 | fn bytes (&self) -> Vec { 12 | let mut bytes = vec![]; 13 | 14 | bytes.extend(self.to_addr.as_bytes()); 15 | bytes.extend(&u64_bytes(&self.value)); 16 | 17 | bytes 18 | } 19 | } 20 | 21 | pub struct Transaction { 22 | pub inputs: Vec, 23 | pub outputs: Vec, 24 | } 25 | 26 | impl Transaction { 27 | pub fn input_value (&self) -> u64 { 28 | self.inputs 29 | .iter() 30 | .map(|input| input.value) 31 | .sum() 32 | } 33 | 34 | pub fn output_value (&self) -> u64 { 35 | self.outputs 36 | .iter() 37 | .map(|output| output.value) 38 | .sum() 39 | } 40 | 41 | pub fn input_hashes (&self) -> HashSet { 42 | self.inputs 43 | .iter() 44 | .map(|input| input.hash()) 45 | .collect::>() 46 | } 47 | 48 | pub fn output_hashes (&self) -> HashSet { 49 | self.outputs 50 | .iter() 51 | .map(|output| output.hash()) 52 | .collect::>() 53 | } 54 | 55 | pub fn is_coinbase (&self) -> bool { 56 | self.inputs.len() == 0 57 | } 58 | } 59 | 60 | impl Hashable for Transaction { 61 | fn bytes (&self) -> Vec { 62 | let mut bytes = vec![]; 63 | 64 | bytes.extend( 65 | self.inputs 66 | .iter() 67 | .flat_map(|input| input.bytes()) 68 | .collect::>() 69 | ); 70 | 71 | bytes.extend( 72 | self.outputs 73 | .iter() 74 | .flat_map(|output| output.bytes()) 75 | .collect::>() 76 | ); 77 | 78 | bytes 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/components/transaction.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Clone)] 5 | pub struct Output { 6 | pub to_addr: Address, 7 | pub value: u64, 8 | } 9 | 10 | impl Hashable for Output { 11 | fn bytes (&self) -> Vec { 12 | let mut bytes = vec![]; 13 | 14 | bytes.extend(self.to_addr.as_bytes()); 15 | bytes.extend(&u64_bytes(&self.value)); 16 | 17 | bytes 18 | } 19 | } 20 | 21 | pub struct Transaction { 22 | pub inputs: Vec, 23 | pub outputs: Vec, 24 | } 25 | 26 | impl Transaction { 27 | pub fn input_value (&self) -> u64 { 28 | self.inputs 29 | .iter() 30 | .map(|input| input.value) 31 | .sum() 32 | } 33 | 34 | pub fn output_value (&self) -> u64 { 35 | self.outputs 36 | .iter() 37 | .map(|output| output.value) 38 | .sum() 39 | } 40 | 41 | pub fn input_hashes (&self) -> HashSet { 42 | self.inputs 43 | .iter() 44 | .map(|input| input.hash()) 45 | .collect::>() 46 | } 47 | 48 | pub fn output_hashes (&self) -> HashSet { 49 | self.outputs 50 | .iter() 51 | .map(|output| output.hash()) 52 | .collect::>() 53 | } 54 | 55 | pub fn is_coinbase (&self) -> bool { 56 | self.inputs.len() == 0 57 | } 58 | } 59 | 60 | impl Hashable for Transaction { 61 | fn bytes (&self) -> Vec { 62 | let mut bytes = vec![]; 63 | 64 | bytes.extend( 65 | self.inputs 66 | .iter() 67 | .flat_map(|input| input.bytes()) 68 | .collect::>() 69 | ); 70 | 71 | bytes.extend( 72 | self.outputs 73 | .iter() 74 | .flat_map(|output| output.bytes()) 75 | .collect::>() 76 | ); 77 | 78 | bytes 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use blockchainlib::*; 2 | 3 | fn main () { 4 | let difficulty = 0x000fffffffffffffffffffffffffffff; 5 | 6 | let mut genesis_block = Block::new(0, now(), vec![0; 32], vec![ 7 | Transaction { 8 | inputs: vec![ ], 9 | outputs: vec![ 10 | transaction::Output { 11 | to_addr: "Alice".to_owned(), 12 | value: 50, 13 | }, 14 | transaction::Output { 15 | to_addr: "Bob".to_owned(), 16 | value: 7, 17 | }, 18 | ], 19 | }, 20 | ], difficulty); 21 | 22 | genesis_block.mine(); 23 | 24 | println!("Mined genesis block {:?}", &genesis_block); 25 | 26 | let mut last_hash = genesis_block.hash.clone(); 27 | 28 | let mut blockchain = Blockchain::new(); 29 | 30 | blockchain.update_with_block(genesis_block).expect("Failed to add genesis block"); 31 | 32 | let mut block = Block::new(1, now(), last_hash, vec![ 33 | Transaction { 34 | inputs: vec![ ], 35 | outputs: vec![ 36 | transaction::Output { 37 | to_addr: "Chris".to_owned(), 38 | value: 536, 39 | }, 40 | ], 41 | }, 42 | Transaction { 43 | inputs: vec![ 44 | blockchain.blocks[0].transactions[0].outputs[0].clone(), 45 | ], 46 | outputs: vec![ 47 | transaction::Output { 48 | to_addr: "Alice".to_owned(), 49 | value: 360, 50 | }, 51 | transaction::Output { 52 | to_addr: "Bob".to_owned(), 53 | value: 12, 54 | }, 55 | ], 56 | }, 57 | ], difficulty); 58 | 59 | block.mine(); 60 | 61 | println!("Mined block {:?}", &block); 62 | 63 | last_hash = block.hash.clone(); 64 | 65 | blockchain.update_with_block(block).expect("Failed to add block"); 66 | } 67 | -------------------------------------------------------------------------------- /src/components/main.rs: -------------------------------------------------------------------------------- 1 | use blockchainlib::*; 2 | 3 | fn main () { 4 | let difficulty = 0x000fffffffffffffffffffffffffffff; 5 | 6 | let mut genesis_block = Block::new(0, now(), vec![0; 32], vec![ 7 | Transaction { 8 | inputs: vec![ ], 9 | outputs: vec![ 10 | transaction::Output { 11 | to_addr: "Alice".to_owned(), 12 | value: 50, 13 | }, 14 | transaction::Output { 15 | to_addr: "Bob".to_owned(), 16 | value: 7, 17 | }, 18 | ], 19 | }, 20 | ], difficulty); 21 | 22 | genesis_block.mine(); 23 | 24 | println!("Mined genesis block {:?}", &genesis_block); 25 | 26 | let mut last_hash = genesis_block.hash.clone(); 27 | 28 | let mut blockchain = Blockchain::new(); 29 | 30 | blockchain.update_with_block(genesis_block).expect("Failed to add genesis block"); 31 | 32 | let mut block = Block::new(1, now(), last_hash, vec![ 33 | Transaction { 34 | inputs: vec![ ], 35 | outputs: vec![ 36 | transaction::Output { 37 | to_addr: "Chris".to_owned(), 38 | value: 536, 39 | }, 40 | ], 41 | }, 42 | Transaction { 43 | inputs: vec![ 44 | blockchain.blocks[0].transactions[0].outputs[0].clone(), 45 | ], 46 | outputs: vec![ 47 | transaction::Output { 48 | to_addr: "Alice".to_owned(), 49 | value: 360, 50 | }, 51 | transaction::Output { 52 | to_addr: "Bob".to_owned(), 53 | value: 12, 54 | }, 55 | ], 56 | }, 57 | ], difficulty); 58 | 59 | block.mine(); 60 | 61 | println!("Mined block {:?}", &block); 62 | 63 | last_hash = block.hash.clone(); 64 | 65 | blockchain.update_with_block(block).expect("Failed to add block"); 66 | } 67 | -------------------------------------------------------------------------------- /src/block.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{ self, Debug, Formatter }; 2 | use super::*; 3 | 4 | pub struct Block { 5 | pub index: u32, 6 | pub timestamp: u128, 7 | pub hash: Hash, 8 | pub prev_block_hash: Hash, 9 | pub nonce: u64, 10 | pub transactions: Vec, 11 | pub difficulty: u128, 12 | } 13 | 14 | impl Debug for Block { 15 | fn fmt (&self, f: &mut Formatter) -> fmt::Result { 16 | write!(f, "Block[{}]: {} at: {} with: {} nonce: {}", 17 | &self.index, 18 | &hex::encode(&self.hash), 19 | &self.timestamp, 20 | &self.transactions.len(), 21 | &self.nonce, 22 | ) 23 | } 24 | } 25 | 26 | impl Block { 27 | pub fn new (index: u32, timestamp: u128, prev_block_hash: Hash, transactions: Vec, difficulty: u128) -> Self { 28 | Block { 29 | index, 30 | timestamp, 31 | hash: vec![0; 32], 32 | prev_block_hash, 33 | nonce: 0, 34 | transactions, 35 | difficulty, 36 | } 37 | } 38 | 39 | pub fn mine (&mut self) { 40 | for nonce_attempt in 0..(u64::max_value()) { 41 | self.nonce = nonce_attempt; 42 | let hash = self.hash(); 43 | if check_difficulty(&hash, self.difficulty) { 44 | self.hash = hash; 45 | return; 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl Hashable for Block { 52 | fn bytes (&self) -> Vec { 53 | let mut bytes = vec![]; 54 | 55 | bytes.extend(&u32_bytes(&self.index)); 56 | bytes.extend(&u128_bytes(&self.timestamp)); 57 | bytes.extend(&self.prev_block_hash); 58 | bytes.extend(&u64_bytes(&self.nonce)); 59 | bytes.extend( 60 | self.transactions 61 | .iter() 62 | .flat_map(|transaction| transaction.bytes()) 63 | .collect::>() 64 | ); 65 | bytes.extend(&u128_bytes(&self.difficulty)); 66 | 67 | bytes 68 | } 69 | } 70 | 71 | pub fn check_difficulty (hash: &Hash, difficulty: u128) -> bool { 72 | difficulty > difficulty_bytes_as_u128(&hash) 73 | } 74 | -------------------------------------------------------------------------------- /src/components/block.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{ self, Debug, Formatter }; 2 | use super::*; 3 | 4 | pub struct Block { 5 | pub index: u32, 6 | pub timestamp: u128, 7 | pub hash: Hash, 8 | pub prev_block_hash: Hash, 9 | pub nonce: u64, 10 | pub transactions: Vec, 11 | pub difficulty: u128, 12 | } 13 | 14 | impl Debug for Block { 15 | fn fmt (&self, f: &mut Formatter) -> fmt::Result { 16 | write!(f, "Block[{}]: {} at: {} with: {} nonce: {}", 17 | &self.index, 18 | &hex::encode(&self.hash), 19 | &self.timestamp, 20 | &self.transactions.len(), 21 | &self.nonce, 22 | ) 23 | } 24 | } 25 | 26 | impl Block { 27 | pub fn new (index: u32, timestamp: u128, prev_block_hash: Hash, transactions: Vec, difficulty: u128) -> Self { 28 | Block { 29 | index, 30 | timestamp, 31 | hash: vec![0; 32], 32 | prev_block_hash, 33 | nonce: 0, 34 | transactions, 35 | difficulty, 36 | } 37 | } 38 | 39 | pub fn mine (&mut self) { 40 | for nonce_attempt in 0..(u64::max_value()) { 41 | self.nonce = nonce_attempt; 42 | let hash = self.hash(); 43 | if check_difficulty(&hash, self.difficulty) { 44 | self.hash = hash; 45 | return; 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl Hashable for Block { 52 | fn bytes (&self) -> Vec { 53 | let mut bytes = vec![]; 54 | 55 | bytes.extend(&u32_bytes(&self.index)); 56 | bytes.extend(&u128_bytes(&self.timestamp)); 57 | bytes.extend(&self.prev_block_hash); 58 | bytes.extend(&u64_bytes(&self.nonce)); 59 | bytes.extend( 60 | self.transactions 61 | .iter() 62 | .flat_map(|transaction| transaction.bytes()) 63 | .collect::>() 64 | ); 65 | bytes.extend(&u128_bytes(&self.difficulty)); 66 | 67 | bytes 68 | } 69 | } 70 | 71 | pub fn check_difficulty (hash: &Hash, difficulty: u128) -> bool { 72 | difficulty > difficulty_bytes_as_u128(&hash) 73 | } 74 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | type Hash = Vec; 2 | type Address = String; 3 | 4 | // Credit: https://stackoverflow.com/a/44378174/2773837 5 | use std::time::{ SystemTime, UNIX_EPOCH }; 6 | 7 | pub fn now () -> u128 { 8 | let duration = SystemTime::now() 9 | .duration_since(UNIX_EPOCH) 10 | .unwrap() 11 | ; 12 | 13 | duration.as_secs() as u128 * 1000 + duration.subsec_millis() as u128 14 | } 15 | 16 | pub fn u32_bytes (u: &u32) -> [u8; 4] { 17 | [ 18 | (u >> 8 * 0x0) as u8, 19 | (u >> 8 * 0x1) as u8, 20 | (u >> 8 * 0x2) as u8, 21 | (u >> 8 * 0x3) as u8, 22 | ] 23 | } 24 | 25 | pub fn u64_bytes (u: &u64) -> [u8; 8] { 26 | [ 27 | (u >> 8 * 0x0) as u8, 28 | (u >> 8 * 0x1) as u8, 29 | (u >> 8 * 0x2) as u8, 30 | (u >> 8 * 0x3) as u8, 31 | 32 | (u >> 8 * 0x4) as u8, 33 | (u >> 8 * 0x5) as u8, 34 | (u >> 8 * 0x6) as u8, 35 | (u >> 8 * 0x7) as u8, 36 | ] 37 | } 38 | 39 | pub fn u128_bytes (u: &u128) -> [u8; 16] { 40 | [ 41 | (u >> 8 * 0x0) as u8, 42 | (u >> 8 * 0x1) as u8, 43 | (u >> 8 * 0x2) as u8, 44 | (u >> 8 * 0x3) as u8, 45 | 46 | (u >> 8 * 0x4) as u8, 47 | (u >> 8 * 0x5) as u8, 48 | (u >> 8 * 0x6) as u8, 49 | (u >> 8 * 0x7) as u8, 50 | 51 | (u >> 8 * 0x8) as u8, 52 | (u >> 8 * 0x9) as u8, 53 | (u >> 8 * 0xa) as u8, 54 | (u >> 8 * 0xb) as u8, 55 | 56 | (u >> 8 * 0xc) as u8, 57 | (u >> 8 * 0xd) as u8, 58 | (u >> 8 * 0xe) as u8, 59 | (u >> 8 * 0xf) as u8, 60 | ] 61 | } 62 | 63 | pub fn difficulty_bytes_as_u128 (v: &Vec) -> u128 { 64 | ((v[31] as u128) << 0xf * 8) | 65 | ((v[30] as u128) << 0xe * 8) | 66 | ((v[29] as u128) << 0xd * 8) | 67 | ((v[28] as u128) << 0xc * 8) | 68 | ((v[27] as u128) << 0xb * 8) | 69 | ((v[26] as u128) << 0xa * 8) | 70 | ((v[25] as u128) << 0x9 * 8) | 71 | ((v[24] as u128) << 0x8 * 8) | 72 | ((v[23] as u128) << 0x7 * 8) | 73 | ((v[22] as u128) << 0x6 * 8) | 74 | ((v[21] as u128) << 0x5 * 8) | 75 | ((v[20] as u128) << 0x4 * 8) | 76 | ((v[19] as u128) << 0x3 * 8) | 77 | ((v[18] as u128) << 0x2 * 8) | 78 | ((v[17] as u128) << 0x1 * 8) | 79 | ((v[16] as u128) << 0x0 * 8) 80 | } 81 | 82 | mod block; 83 | pub use crate::block::Block; 84 | mod hashable; 85 | pub use crate::hashable::Hashable; 86 | mod blockchain; 87 | pub use crate::blockchain::Blockchain; 88 | pub mod transaction; 89 | pub use crate::transaction::Transaction; 90 | -------------------------------------------------------------------------------- /src/components/lib.rs: -------------------------------------------------------------------------------- 1 | type Hash = Vec; 2 | type Address = String; 3 | 4 | // Credit: https://stackoverflow.com/a/44378174/2773837 5 | use std::time::{ SystemTime, UNIX_EPOCH }; 6 | 7 | pub fn now () -> u128 { 8 | let duration = SystemTime::now() 9 | .duration_since(UNIX_EPOCH) 10 | .unwrap() 11 | ; 12 | 13 | duration.as_secs() as u128 * 1000 + duration.subsec_millis() as u128 14 | } 15 | 16 | pub fn u32_bytes (u: &u32) -> [u8; 4] { 17 | [ 18 | (u >> 8 * 0x0) as u8, 19 | (u >> 8 * 0x1) as u8, 20 | (u >> 8 * 0x2) as u8, 21 | (u >> 8 * 0x3) as u8, 22 | ] 23 | } 24 | 25 | pub fn u64_bytes (u: &u64) -> [u8; 8] { 26 | [ 27 | (u >> 8 * 0x0) as u8, 28 | (u >> 8 * 0x1) as u8, 29 | (u >> 8 * 0x2) as u8, 30 | (u >> 8 * 0x3) as u8, 31 | 32 | (u >> 8 * 0x4) as u8, 33 | (u >> 8 * 0x5) as u8, 34 | (u >> 8 * 0x6) as u8, 35 | (u >> 8 * 0x7) as u8, 36 | ] 37 | } 38 | 39 | pub fn u128_bytes (u: &u128) -> [u8; 16] { 40 | [ 41 | (u >> 8 * 0x0) as u8, 42 | (u >> 8 * 0x1) as u8, 43 | (u >> 8 * 0x2) as u8, 44 | (u >> 8 * 0x3) as u8, 45 | 46 | (u >> 8 * 0x4) as u8, 47 | (u >> 8 * 0x5) as u8, 48 | (u >> 8 * 0x6) as u8, 49 | (u >> 8 * 0x7) as u8, 50 | 51 | (u >> 8 * 0x8) as u8, 52 | (u >> 8 * 0x9) as u8, 53 | (u >> 8 * 0xa) as u8, 54 | (u >> 8 * 0xb) as u8, 55 | 56 | (u >> 8 * 0xc) as u8, 57 | (u >> 8 * 0xd) as u8, 58 | (u >> 8 * 0xe) as u8, 59 | (u >> 8 * 0xf) as u8, 60 | ] 61 | } 62 | 63 | pub fn difficulty_bytes_as_u128 (v: &Vec) -> u128 { 64 | ((v[31] as u128) << 0xf * 8) | 65 | ((v[30] as u128) << 0xe * 8) | 66 | ((v[29] as u128) << 0xd * 8) | 67 | ((v[28] as u128) << 0xc * 8) | 68 | ((v[27] as u128) << 0xb * 8) | 69 | ((v[26] as u128) << 0xa * 8) | 70 | ((v[25] as u128) << 0x9 * 8) | 71 | ((v[24] as u128) << 0x8 * 8) | 72 | ((v[23] as u128) << 0x7 * 8) | 73 | ((v[22] as u128) << 0x6 * 8) | 74 | ((v[21] as u128) << 0x5 * 8) | 75 | ((v[20] as u128) << 0x4 * 8) | 76 | ((v[19] as u128) << 0x3 * 8) | 77 | ((v[18] as u128) << 0x2 * 8) | 78 | ((v[17] as u128) << 0x1 * 8) | 79 | ((v[16] as u128) << 0x0 * 8) 80 | } 81 | 82 | mod block; 83 | pub use crate::block::Block; 84 | mod hashable; 85 | pub use crate::hashable::Hashable; 86 | mod blockchain; 87 | pub use crate::blockchain::Blockchain; 88 | pub mod transaction; 89 | pub use crate::transaction::Transaction; 90 | -------------------------------------------------------------------------------- /src/blockchain.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Debug)] 5 | pub enum BlockValidationErr { 6 | MismatchedIndex, 7 | InvalidHash, 8 | AchronologicalTimestamp, 9 | MismatchedPreviousHash, 10 | InvalidGenesisBlockFormat, 11 | InvalidInput, 12 | InsufficientInputValue, 13 | InvalidCoinbaseTransaction, 14 | } 15 | 16 | pub struct Blockchain { 17 | pub blocks: Vec, 18 | unspent_outputs: HashSet, 19 | } 20 | 21 | impl Blockchain { 22 | pub fn new () -> Self { 23 | Blockchain { 24 | blocks: vec![], 25 | unspent_outputs: HashSet::new(), 26 | } 27 | } 28 | 29 | pub fn update_with_block (&mut self, block: Block) -> Result<(), BlockValidationErr> { 30 | let i = self.blocks.len(); 31 | 32 | if block.index != i as u32 { 33 | return Err(BlockValidationErr::MismatchedIndex); 34 | } else if !block::check_difficulty(&block.hash(), block.difficulty) { 35 | return Err(BlockValidationErr::InvalidHash); 36 | } else if i != 0 { 37 | // Not genesis block 38 | let prev_block = &self.blocks[i - 1]; 39 | if block.timestamp <= prev_block.timestamp { 40 | return Err(BlockValidationErr::AchronologicalTimestamp); 41 | } else if block.prev_block_hash != prev_block.hash { 42 | return Err(BlockValidationErr::MismatchedPreviousHash); 43 | } 44 | } else { 45 | // Genesis block 46 | if block.prev_block_hash != vec![0; 32] { 47 | return Err(BlockValidationErr::InvalidGenesisBlockFormat); 48 | } 49 | } 50 | 51 | if let Some((coinbase, transactions)) = block.transactions.split_first() { 52 | if !coinbase.is_coinbase() { 53 | return Err(BlockValidationErr::InvalidCoinbaseTransaction); 54 | } 55 | 56 | let mut block_spent: HashSet = HashSet::new(); 57 | let mut block_created: HashSet = HashSet::new(); 58 | let mut total_fee = 0; 59 | 60 | for transaction in transactions { 61 | let input_hashes = transaction.input_hashes(); 62 | 63 | if 64 | !(&input_hashes - &self.unspent_outputs).is_empty() || 65 | !(&input_hashes & &block_spent).is_empty() 66 | { 67 | return Err(BlockValidationErr::InvalidInput); 68 | } 69 | 70 | let input_value = transaction.input_value(); 71 | let output_value = transaction.output_value(); 72 | 73 | if output_value > input_value { 74 | return Err(BlockValidationErr::InsufficientInputValue); 75 | } 76 | 77 | let fee = input_value - output_value; 78 | 79 | total_fee += fee; 80 | 81 | block_spent.extend(input_hashes); 82 | block_created.extend(transaction.output_hashes()); 83 | } 84 | 85 | if coinbase.output_value() < total_fee { 86 | return Err(BlockValidationErr::InvalidCoinbaseTransaction); 87 | } else { 88 | block_created.extend(coinbase.output_hashes()); 89 | } 90 | 91 | self.unspent_outputs.retain(|output| !block_spent.contains(output)); 92 | self.unspent_outputs.extend(block_created); 93 | } 94 | 95 | self.blocks.push(block); 96 | 97 | Ok(()) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/components/blockchain.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashSet; 3 | 4 | #[derive(Debug)] 5 | pub enum BlockValidationErr { 6 | MismatchedIndex, 7 | InvalidHash, 8 | AchronologicalTimestamp, 9 | MismatchedPreviousHash, 10 | InvalidGenesisBlockFormat, 11 | InvalidInput, 12 | InsufficientInputValue, 13 | InvalidCoinbaseTransaction, 14 | } 15 | 16 | pub struct Blockchain { 17 | pub blocks: Vec, 18 | unspent_outputs: HashSet, 19 | } 20 | 21 | impl Blockchain { 22 | pub fn new () -> Self { 23 | Blockchain { 24 | blocks: vec![], 25 | unspent_outputs: HashSet::new(), 26 | } 27 | } 28 | 29 | pub fn update_with_block (&mut self, block: Block) -> Result<(), BlockValidationErr> { 30 | let i = self.blocks.len(); 31 | 32 | if block.index != i as u32 { 33 | return Err(BlockValidationErr::MismatchedIndex); 34 | } else if !block::check_difficulty(&block.hash(), block.difficulty) { 35 | return Err(BlockValidationErr::InvalidHash); 36 | } else if i != 0 { 37 | // Not genesis block 38 | let prev_block = &self.blocks[i - 1]; 39 | if block.timestamp <= prev_block.timestamp { 40 | return Err(BlockValidationErr::AchronologicalTimestamp); 41 | } else if block.prev_block_hash != prev_block.hash { 42 | return Err(BlockValidationErr::MismatchedPreviousHash); 43 | } 44 | } else { 45 | // Genesis block 46 | if block.prev_block_hash != vec![0; 32] { 47 | return Err(BlockValidationErr::InvalidGenesisBlockFormat); 48 | } 49 | } 50 | 51 | if let Some((coinbase, transactions)) = block.transactions.split_first() { 52 | if !coinbase.is_coinbase() { 53 | return Err(BlockValidationErr::InvalidCoinbaseTransaction); 54 | } 55 | 56 | let mut block_spent: HashSet = HashSet::new(); 57 | let mut block_created: HashSet = HashSet::new(); 58 | let mut total_fee = 0; 59 | 60 | for transaction in transactions { 61 | let input_hashes = transaction.input_hashes(); 62 | 63 | if 64 | !(&input_hashes - &self.unspent_outputs).is_empty() || 65 | !(&input_hashes & &block_spent).is_empty() 66 | { 67 | return Err(BlockValidationErr::InvalidInput); 68 | } 69 | 70 | let input_value = transaction.input_value(); 71 | let output_value = transaction.output_value(); 72 | 73 | if output_value > input_value { 74 | return Err(BlockValidationErr::InsufficientInputValue); 75 | } 76 | 77 | let fee = input_value - output_value; 78 | 79 | total_fee += fee; 80 | 81 | block_spent.extend(input_hashes); 82 | block_created.extend(transaction.output_hashes()); 83 | } 84 | 85 | if coinbase.output_value() < total_fee { 86 | return Err(BlockValidationErr::InvalidCoinbaseTransaction); 87 | } else { 88 | block_created.extend(coinbase.output_hashes()); 89 | } 90 | 91 | self.unspent_outputs.retain(|output| !block_spent.contains(output)); 92 | self.unspent_outputs.extend(block_created); 93 | } 94 | 95 | self.blocks.push(block); 96 | 97 | Ok(()) 98 | } 99 | } 100 | --------------------------------------------------------------------------------