snap_coin/core/
block.rs

1use bincode::{Decode, Encode, error::EncodeError};
2use num_bigint::BigUint;
3use rand::Rng;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use crate::{
8    core::{difficulty::calculate_block_difficulty, transaction::Transaction},
9    crypto::{Hash, address_inclusion_filter::AddressInclusionFilter, merkle_tree::MerkleTree},
10};
11
12pub const MAX_TRANSACTIONS_PER_BLOCK: usize = 500;
13
14#[derive(Error, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
15pub enum BlockError {
16    #[error("Block is missing required metadata")]
17    IncompleteBlock,
18
19    #[error("Encoding error")]
20    EncodeError,
21
22    #[error("Block hash is invalid")]
23    InvalidBlockHash,
24
25    #[error("Block difficulties don't match real difficulties")]
26    DifficultyMismatch,
27
28    #[error("Block pow difficulty is not up to target")]
29    BlockPowDifficultyIncorrect,
30
31    #[error("Transaction is incomplete")]
32    IncompleteTransaction,
33
34    #[error("Merkle root tree is invalid")]
35    InvalidMerkleTreeRoot,
36}
37
38/// Stores transaction, difficulties, its hash, and its nonce
39/// The hash can be often used for indexing, however can only be trusted if this node checked this block already
40#[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug)]
41pub struct Block {
42    pub transactions: Vec<Transaction>,
43    pub timestamp: u64,
44    pub nonce: u64,
45    pub meta: BlockMetadata,
46}
47
48impl Block {
49    /// Create a new block timestamped now, with a set of transactions, specifying transaction difficulty and block difficulty
50    pub fn new_block_now(
51        transactions: Vec<Transaction>,
52        block_pow_difficulty: &[u8; 32],
53        tx_pow_difficulty: &[u8; 32],
54        previous_block: Hash,
55        merkle_tree_root: &[u8; 32],
56        address_inclusion_filter: AddressInclusionFilter,
57    ) -> Self {
58        Block {
59            transactions,
60            timestamp: chrono::Utc::now().timestamp() as u64,
61            nonce: 0,
62            meta: BlockMetadata {
63                block_pow_difficulty: *block_pow_difficulty,
64                tx_pow_difficulty: *tx_pow_difficulty,
65                previous_block,
66                hash: None,
67                merkle_tree_root: *merkle_tree_root,
68                address_inclusion_filter,
69            },
70        }
71    }
72
73    /// Get this blocks hashing buffer required to mine this transaction. Essentially makes sure that any hash attached to this block is not included in the block hashing buffer
74    /// WARNING: Slow
75    pub fn get_hashing_buf(&self) -> Result<Vec<u8>, EncodeError> {
76        let mut hash_less_block = self.clone();
77        hash_less_block.meta.hash = None; // Remove hash
78
79        // Remove all transaction inputs because, we can just hash the transaction hash and keep the integrity
80        for transaction in &mut hash_less_block.transactions {
81            transaction.inputs = vec![];
82            transaction.outputs = vec![];
83        }
84        bincode::encode_to_vec(hash_less_block, bincode::config::standard())
85    }
86
87    /// Mine this block and attach its hash.
88    /// DEPRECATED: This is single threaded and cannot be used for actual mining as proper, multi-threaded mining machines outperform this by absolute miles
89    #[deprecated]
90    pub fn compute_pow(&mut self) -> Result<(), EncodeError> {
91        let tx_difficulty_big_int = BigUint::from_bytes_be(&calculate_block_difficulty(
92            &self.meta.block_pow_difficulty,
93            self.transactions.len(),
94        ));
95        let mut rng: rand::prelude::ThreadRng = rand::rng();
96        loop {
97            self.nonce = rng.random();
98            let hashing_buf = self.get_hashing_buf()?;
99            if BigUint::from_bytes_be(&*Hash::new(&hashing_buf)) <= tx_difficulty_big_int {
100                self.meta.hash = Some(Hash::new(&hashing_buf));
101                return Ok(());
102            }
103        }
104    }
105
106    /// Checks if block meta is valid
107    pub fn check_meta(&self) -> Result<(), BlockError> {
108        self.check_completeness()?;
109        self.validate_block_hash()?;
110        self.validate_block_hash()?;
111        self.validate_merkle_tree()?;
112        Ok(())
113    }
114
115    /// Checks if this block is complete, and has all required fields to be valid on a blockchain
116    pub fn check_completeness(&self) -> Result<(), BlockError> {
117        self.meta
118            .hash
119            .ok_or(BlockError::IncompleteBlock)
120            .map(|_| ())?;
121        Ok(())
122    }
123
124    /// Checks if the attached block hash is valid
125    pub fn validate_block_hash(&self) -> Result<(), BlockError> {
126        self.check_completeness()?;
127        if !self
128            .meta
129            .hash
130            .ok_or(BlockError::IncompleteBlock)?
131            .compare_with_data(
132                &self
133                    .get_hashing_buf()
134                    .map_err(|_| BlockError::EncodeError)?,
135            )
136        {
137            return Err(BlockError::InvalidBlockHash);
138        }
139        Ok(())
140    }
141
142    /// Checks if the passed difficulties match the blocks difficulties (true = valid, false = invalid)
143    pub fn validate_difficulties(
144        &self,
145        real_block_pow_difficulty: &[u8; 32],
146        real_tx_pow_difficulty: &[u8; 32],
147    ) -> Result<(), BlockError> {
148        if self.meta.block_pow_difficulty != *real_block_pow_difficulty
149            || self.meta.tx_pow_difficulty != *real_tx_pow_difficulty
150        {
151            return Err(BlockError::DifficultyMismatch);
152        }
153        if BigUint::from_bytes_be(&*self.meta.hash.unwrap())
154            > BigUint::from_bytes_be(&calculate_block_difficulty(
155                real_block_pow_difficulty,
156                self.transactions.len(),
157            ))
158        {
159            return Err(BlockError::BlockPowDifficultyIncorrect);
160        }
161        Ok(())
162    }
163
164    /// Check if merkle tree root is correctly calculated
165    pub fn validate_merkle_tree(&self) -> Result<(), BlockError> {
166        let mut ids = vec![];
167        for tx in &self.transactions {
168            tx.check_completeness()
169                .map_err(|_| BlockError::IncompleteTransaction)?;
170            ids.push(tx.transaction_id.unwrap());
171        }
172        let tree = MerkleTree::build(&ids);
173        if tree.root_hash() != self.meta.merkle_tree_root {
174            return Err(BlockError::InvalidMerkleTreeRoot);
175        }
176        Ok(())
177    }
178
179    pub fn address_count(&self) -> usize {
180        self.transactions
181            .iter()
182            .fold(0, |acc, tx| acc + tx.address_count())
183    }
184}
185
186// Represents all block data that is not essential to it's existence (however required)
187#[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug)]
188pub struct BlockMetadata {
189    pub block_pow_difficulty: [u8; 32],
190    pub tx_pow_difficulty: [u8; 32],
191    pub previous_block: Hash,
192    pub hash: Option<Hash>,
193    pub merkle_tree_root: [u8; 32],
194    pub address_inclusion_filter: AddressInclusionFilter,
195}