forked from GeekLaunch/blockchain-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblockchain.rs
99 lines (81 loc) · 3.1 KB
/
blockchain.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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 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);
}
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);
}
self.blocks.push(block);
Ok(())
}
}