snap_coin/core/
block.rs

1use bincode::{Decode, Encode, error::EncodeError};
2use num_bigint::BigUint;
3use rand::Rng;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    core::{difficulty::calculate_block_difficulty, transaction::Transaction},
8    crypto::Hash,
9};
10
11pub const MAX_TRANSACTIONS: usize = 500;
12
13/// Stores transaction, difficulties, its hash, and its nonce
14/// The hash can be often used for indexing, however can only be trusted if this node checked this block already
15#[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug)]
16pub struct Block {
17    pub transactions: Vec<Transaction>,
18    pub timestamp: u64,
19    pub nonce: u64,
20    pub block_pow_difficulty: [u8; 32],
21    pub tx_pow_difficulty: [u8; 32],
22    pub previous_block: Hash,
23    pub hash: Option<Hash>,
24}
25
26impl Block {
27    /// Create a new block timestamped now, with a set of transactions, specifying transaction difficulty and block difficulty
28    pub fn new_block_now(
29        transactions: Vec<Transaction>,
30        block_pow_difficulty: &[u8; 32],
31        tx_pow_difficulty: &[u8; 32],
32        previous_block: Hash
33    ) -> Self {
34        Block {
35            transactions,
36            timestamp: chrono::Utc::now().timestamp() as u64,
37            nonce: 0,
38            block_pow_difficulty: *block_pow_difficulty,
39            tx_pow_difficulty: *tx_pow_difficulty,
40            previous_block,
41            hash: None,
42        }
43    }
44
45    /// 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
46    /// WARNING: Slow
47    pub fn get_hashing_buf(&self) -> Result<Vec<u8>, EncodeError> {
48        let mut hash_less_block = self.clone();
49        hash_less_block.hash = None; // Remove hash
50        
51        // Remove all transaction inputs because, we can just hash the transaction hash and keep the integrity
52        for transaction in &mut hash_less_block.transactions {
53            transaction.inputs = vec![];
54            transaction.outputs = vec![];
55        }
56        bincode::encode_to_vec(hash_less_block, bincode::config::standard())
57    }
58
59    /// Mine this block and attach its hash.
60    /// DEPRECATED: This is single threaded and cannot be used for actual mining as proper, multi-threaded mining machines outperform this by absolute miles
61    #[deprecated]
62    pub fn compute_pow(&mut self) -> Result<(), EncodeError> {
63        let tx_difficulty_big_int = BigUint::from_bytes_be(&calculate_block_difficulty(
64            &self.block_pow_difficulty,
65            self.transactions.len(),
66        ));
67        let mut rng: rand::prelude::ThreadRng = rand::rng();
68        loop {
69            self.nonce = rng.random();
70            let hashing_buf = self.get_hashing_buf()?;
71            if BigUint::from_bytes_be(&*Hash::new(&hashing_buf)) <= tx_difficulty_big_int {
72                self.hash = Some(Hash::new(&hashing_buf));
73                return Ok(());
74            }
75        }
76    }
77}