use serde::Serialize;
use super::*;
use crate::transaction::Put;
use crate::transaction::IO;
use std::collections::HashSet;
#[derive(Debug,Serialize)]
pub struct Blockchain {
pub blocks: Vec<Block >,
unspent_outputs: HashSet<Hash>,
}
impl Default for Blockchain {
fn default() -> Self {
Blockchain {
blocks: vec![],
unspent_outputs: HashSet::new(),
}
}
}
impl Blockchain {
pub fn new() -> Self {
Blockchain {
blocks: vec![],
unspent_outputs: HashSet::new(),
}
}
pub fn blockchain_update_with_block(&mut self, block: Block ) -> Result<&Vec<Block>, CustomError> {
let i = &self.blocks.len();
if block.index != *i as u32 {
return Err(CustomError::BlockValidation(
BlockValidationError::MismatchedIndex,
));
} else if !block::blockchain_check_difficulty(&block.hash(), block.difficulty) {
return Err(CustomError::BlockValidation(
BlockValidationError::InvalidHash,
));
} else if *i != 0 {
let prev_block = &self.blocks[i - 1];
if block.timestamp <= prev_block.timestamp {
return Err(CustomError::BlockValidation(
BlockValidationError::AchronologicalTimestamp,
));
} else if block.prev_block_hash != prev_block.hash {
return Err(CustomError::BlockValidation(
BlockValidationError::MismatchedPreviousHash,
));
}
} else {
if block.prev_block_hash != vec![0; 32] {
return Err(CustomError::BlockValidation(
BlockValidationError::InvalidGenesisBlockFormat,
));
}
}
if let Some((coinbase, option_transactions)) = block.transactions.split_first() {
if !coinbase.is_coinbase() {
return Err(CustomError::BlockValidation(
BlockValidationError::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 option_transactions {
let input_hashes = transaction.returns_closure_io_hash(&IO::Input);
let output_hashes = transaction.returns_closure_io_hash(&IO::Output);
if !(&input_hashes() - &self.unspent_outputs).is_empty()
|| !(&input_hashes() & &block_spent).is_empty()
{
if *i==0 {
return Err(CustomError::BlockValidation(
BlockValidationError::InvalidInput
));
}
}
let input_value = transaction.returns_closure_io(&IO::Input);
let output_value = transaction.returns_closure_io(&IO::Output);
if &output_value() > &input_value() {
return Err(CustomError::BlockValidation(
BlockValidationError::InsufficientInputValue,
));
}
let fee = &input_value() - &output_value();
total_fee += fee;
block_spent.extend(input_hashes());
block_created.extend(output_hashes());
}
let coinbase_output_value = coinbase.returns_closure_io(&IO::Output);
if coinbase_output_value() < total_fee {
return Err(CustomError::BlockValidation(
BlockValidationError::InvalidCoinbaseTransactionFee,
));
} else {
let coinbase_output_hashes = coinbase.returns_closure_io_hash(&IO::Output);
block_created.extend(coinbase_output_hashes());
}
self.unspent_outputs
.retain(|output| !block_spent.contains(output));
self.unspent_outputs.extend(block_created);
}
println!("**BlOcKcHaIn SiGnAls:**\n{:?}\n", &block);
let _ = &self.blocks.push(block);
Ok(&self.blocks)
}
}