snap_coin/core/
difficulty.rs1use std::sync::RwLock;
2
3use crate::{
4 core::{
5 block::Block,
6 economics::{DIFFICULTY_DECAY_PER_TRANSACTION, MAX_DIFF_CHANGE, TARGET_TIME, TX_TARGET},
7 utils::{clamp_f, max_256_bui},
8 },
9 economics::MEMPOOL_PRESSURE_PER_TRANSACTION,
10};
11use bincode::{Decode, Encode};
12use num_bigint::BigUint;
13
14pub const STARTING_BLOCK_DIFFICULTY: [u8; 32] = [u8::MAX; 32];
15pub const STARTING_TX_DIFFICULTY: [u8; 32] = [u8::MAX; 32];
16
17#[derive(Encode, Decode, Debug)]
18pub struct DifficultyState {
19 pub block_difficulty: RwLock<[u8; 32]>,
20 pub transaction_difficulty: RwLock<[u8; 32]>,
21 pub last_timestamp: RwLock<u64>,
22}
23
24impl DifficultyState {
26 pub fn new_default() -> Self {
28 DifficultyState {
29 block_difficulty: RwLock::new(STARTING_BLOCK_DIFFICULTY),
30 transaction_difficulty: RwLock::new(STARTING_TX_DIFFICULTY),
31 last_timestamp: RwLock::new(0),
32 }
33 }
34
35 pub fn update_difficulty(&self, new_block: &Block) {
37 let time_ratio = (clamp_f(
39 (new_block
40 .timestamp
41 .saturating_sub(*self.last_timestamp.read().unwrap())) as f64
42 / TARGET_TIME as f64,
43 MAX_DIFF_CHANGE,
44 2.0 - MAX_DIFF_CHANGE,
45 ) * 1000.0) as u64;
46
47 let mut block_big = BigUint::from_bytes_be(&*self.block_difficulty.read().unwrap());
48 block_big = block_big * BigUint::from(time_ratio) / BigUint::from(1000u64);
49 *self.block_difficulty.write().unwrap() =
50 biguint_to_32_bytes(block_big.min(max_256_bui()).max(BigUint::ZERO));
51
52 let tx_ratio = (clamp_f(
54 TX_TARGET as f64 / new_block.transactions.len() as f64,
55 MAX_DIFF_CHANGE,
56 2.0 - MAX_DIFF_CHANGE,
57 ) * 1000.0) as u64;
58
59 let mut tx_big = BigUint::from_bytes_be(&*self.transaction_difficulty.read().unwrap());
60 tx_big = tx_big * BigUint::from(tx_ratio) / BigUint::from(1000u64);
61 *self.transaction_difficulty.write().unwrap() =
62 biguint_to_32_bytes(tx_big.min(max_256_bui()).max(BigUint::ZERO));
63
64 *self.last_timestamp.write().unwrap() = new_block.timestamp;
66 }
67
68 pub fn get_block_difficulty(&self) -> [u8; 32] {
69 *self.block_difficulty.read().unwrap()
70 }
71
72 pub fn get_transaction_difficulty(&self) -> [u8; 32] {
73 *self.transaction_difficulty.read().unwrap()
74 }
75}
76
77impl Clone for DifficultyState {
78 fn clone(&self) -> Self {
79 Self {
80 block_difficulty: RwLock::new(*self.block_difficulty.read().unwrap()),
81 transaction_difficulty: RwLock::new(*self.transaction_difficulty.read().unwrap()),
82 last_timestamp: RwLock::new(*self.last_timestamp.read().unwrap()),
83 }
84 }
85}
86
87pub fn calculate_block_difficulty(block_difficulty: &[u8; 32], tx_count: usize) -> [u8; 32] {
89 let difficulty = BigUint::from_bytes_be(block_difficulty);
90 biguint_to_32_bytes(
91 (difficulty
92 * BigUint::from(
93 ((1f64 + DIFFICULTY_DECAY_PER_TRANSACTION * tx_count as f64) * 1000f64) as u64,
94 )
95 / BigUint::from(1000u64))
96 .min(max_256_bui()),
97 )
98}
99
100pub fn calculate_live_transaction_difficulty(
102 transaction_difficulty: &[u8; 32],
103 mempool_size: usize,
104) -> [u8; 32] {
105 let difficulty = BigUint::from_bytes_be(transaction_difficulty);
106
107 let decay = (mempool_size as f64) * MEMPOOL_PRESSURE_PER_TRANSACTION;
109 let decay = decay.clamp(0.0, 1.0); let factor = (1.0 - decay) * 1_000_000.0; let mut new_difficulty = &difficulty * BigUint::from(factor as u64);
114 new_difficulty /= BigUint::from(1_000_000u64);
115
116 biguint_to_32_bytes(new_difficulty)
117}
118
119fn biguint_to_32_bytes(value: BigUint) -> [u8; 32] {
120 let mut bytes = value.to_bytes_be();
121 if bytes.len() < 32 {
122 let mut padded = vec![0u8; 32 - bytes.len()];
123 padded.extend(bytes);
124 bytes = padded;
125 } else if bytes.len() > 32 {
126 bytes = bytes[bytes.len() - 32..].to_vec();
127 }
128 let mut array = [0u8; 32];
129 array.copy_from_slice(&bytes);
130 array
131}