forked from GeekLaunch/blockchain-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fifth video - transactions and outputs
- Loading branch information
Showing
6 changed files
with
236 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,99 @@ | ||
use super::*; | ||
use std::collections::HashSet; | ||
|
||
#[derive(Debug)] | ||
pub enum BlockValidationErr { | ||
MismatchedIndex, | ||
InvalidHash, | ||
AchronologicalTimestamp, | ||
MismatchedPreviousHash, | ||
InvalidGenesisBlockFormat, | ||
InvalidInput, | ||
InsufficientInputValue, | ||
InvalidCoinbaseTransaction, | ||
} | ||
|
||
pub struct Blockchain { | ||
pub blocks: Vec<Block>, | ||
unspent_outputs: HashSet<Hash>, | ||
} | ||
|
||
impl Blockchain { | ||
pub fn verify (&self) -> bool { | ||
for (i, block) in self.blocks.iter().enumerate() { | ||
if block.index != i as u32 { | ||
println!("Index mismatch {} != {}", | ||
&block.index, | ||
&i, | ||
); | ||
return false; | ||
} else if !block::check_difficulty(&block.hash(), block.difficulty) { | ||
println!("Difficulty fail"); | ||
return false; | ||
} else if i != 0 { | ||
// Not genesis block | ||
let prev_block = &self.blocks[i - 1]; | ||
if block.timestamp <= prev_block.timestamp { | ||
println!("Time did not increase"); | ||
return false; | ||
} else if block.prev_block_hash != prev_block.hash { | ||
println!("Hash mismatch"); | ||
return false; | ||
pub fn new () -> Self { | ||
Blockchain { | ||
blocks: vec![], | ||
unspent_outputs: HashSet::new(), | ||
} | ||
} | ||
|
||
pub fn update_with_block (&mut self, block: Block) -> Result<(), BlockValidationErr> { | ||
let i = self.blocks.len(); | ||
|
||
if block.index != i as u32 { | ||
return Err(BlockValidationErr::MismatchedIndex); | ||
} else if !block::check_difficulty(&block.hash(), block.difficulty) { | ||
return Err(BlockValidationErr::InvalidHash); | ||
} else if i != 0 { | ||
// Not genesis block | ||
let prev_block = &self.blocks[i - 1]; | ||
if block.timestamp <= prev_block.timestamp { | ||
return Err(BlockValidationErr::AchronologicalTimestamp); | ||
} else if block.prev_block_hash != prev_block.hash { | ||
return Err(BlockValidationErr::MismatchedPreviousHash); | ||
} | ||
} else { | ||
// Genesis block | ||
if block.prev_block_hash != vec![0; 32] { | ||
return Err(BlockValidationErr::InvalidGenesisBlockFormat); | ||
} | ||
} | ||
|
||
if let Some((coinbase, transactions)) = block.transactions.split_first() { | ||
if !coinbase.is_coinbase() { | ||
return Err(BlockValidationErr::InvalidCoinbaseTransaction); | ||
} | ||
|
||
let mut block_spent: HashSet<Hash> = HashSet::new(); | ||
let mut block_created: HashSet<Hash> = HashSet::new(); | ||
let mut total_fee = 0; | ||
|
||
for transaction in transactions { | ||
let input_hashes = transaction.input_hashes(); | ||
|
||
if | ||
!(&input_hashes - &self.unspent_outputs).is_empty() || | ||
!(&input_hashes & &block_spent).is_empty() | ||
{ | ||
return Err(BlockValidationErr::InvalidInput); | ||
} | ||
} else { | ||
// Genesis block | ||
if block.prev_block_hash != vec![0; 32] { | ||
println!("Genesis block prev_block_hash invalid"); | ||
return false; | ||
|
||
let input_value = transaction.input_value(); | ||
let output_value = transaction.output_value(); | ||
|
||
if output_value > input_value { | ||
return Err(BlockValidationErr::InsufficientInputValue); | ||
} | ||
|
||
let fee = input_value - output_value; | ||
|
||
total_fee += fee; | ||
|
||
block_spent.extend(input_hashes); | ||
block_created.extend(transaction.output_hashes()); | ||
} | ||
|
||
if coinbase.output_value() < total_fee { | ||
return Err(BlockValidationErr::InvalidCoinbaseTransaction); | ||
} else { | ||
block_created.extend(coinbase.output_hashes()); | ||
} | ||
|
||
self.unspent_outputs.retain(|output| !block_spent.contains(output)); | ||
self.unspent_outputs.extend(block_created); | ||
} | ||
|
||
true | ||
self.blocks.push(block); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
use super::*; | ||
|
||
pub trait Hashable { | ||
fn bytes (&self) -> Vec<u8>; | ||
|
||
fn hash (&self) -> Vec<u8> { | ||
fn hash (&self) -> Hash { | ||
crypto_hash::digest(crypto_hash::Algorithm::SHA256, &self.bytes()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
use super::*; | ||
use std::collections::HashSet; | ||
|
||
#[derive(Clone)] | ||
pub struct Output { | ||
pub to_addr: Address, | ||
pub value: u64, | ||
} | ||
|
||
impl Hashable for Output { | ||
fn bytes (&self) -> Vec<u8> { | ||
let mut bytes = vec![]; | ||
|
||
bytes.extend(self.to_addr.as_bytes()); | ||
bytes.extend(&u64_bytes(&self.value)); | ||
|
||
bytes | ||
} | ||
} | ||
|
||
pub struct Transaction { | ||
pub inputs: Vec<Output>, | ||
pub outputs: Vec<Output>, | ||
} | ||
|
||
impl Transaction { | ||
pub fn input_value (&self) -> u64 { | ||
self.inputs | ||
.iter() | ||
.map(|input| input.value) | ||
.sum() | ||
} | ||
|
||
pub fn output_value (&self) -> u64 { | ||
self.outputs | ||
.iter() | ||
.map(|output| output.value) | ||
.sum() | ||
} | ||
|
||
pub fn input_hashes (&self) -> HashSet<Hash> { | ||
self.inputs | ||
.iter() | ||
.map(|input| input.hash()) | ||
.collect::<HashSet<Hash>>() | ||
} | ||
|
||
pub fn output_hashes (&self) -> HashSet<Hash> { | ||
self.outputs | ||
.iter() | ||
.map(|output| output.hash()) | ||
.collect::<HashSet<Hash>>() | ||
} | ||
|
||
pub fn is_coinbase (&self) -> bool { | ||
self.inputs.len() == 0 | ||
} | ||
} | ||
|
||
impl Hashable for Transaction { | ||
fn bytes (&self) -> Vec<u8> { | ||
let mut bytes = vec![]; | ||
|
||
bytes.extend( | ||
self.inputs | ||
.iter() | ||
.flat_map(|input| input.bytes()) | ||
.collect::<Vec<u8>>() | ||
); | ||
|
||
bytes.extend( | ||
self.outputs | ||
.iter() | ||
.flat_map(|output| output.bytes()) | ||
.collect::<Vec<u8>>() | ||
); | ||
|
||
bytes | ||
} | ||
} |