main.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. sync::{
  20. atomic::{AtomicBool, AtomicU32, Ordering},
  21. Arc,
  22. },
  23. thread,
  24. time::Instant,
  25. };
  26. use darkfi::{util::time::Timestamp, Result};
  27. use darkfi_sdk::{
  28. crypto::MerkleTree,
  29. pasta::{group::ff::FromUniformBytes, pallas},
  30. };
  31. use darkfi_serial::{async_trait, Encodable, SerialDecodable, SerialEncodable};
  32. use rand::{rngs::OsRng, Rng};
  33. use randomx::{RandomXCache, RandomXDataset, RandomXFlags, RandomXVM};
  34. /// Constant genesis block string used as the previous block hash
  35. const GENESIS: &[u8] = b"genesis";
  36. /// The target mining difficulty
  37. const DIFFICULTY: usize = 1;
  38. /// The output length of the BLAKE2b hash in bytes
  39. const HASH_LEN: usize = 32;
  40. /// The amount of blocks the main loop will mine until the program exits
  41. const N_BLOCKS: usize = 5;
  42. #[derive(Clone, SerialEncodable, SerialDecodable)]
  43. struct Transaction(Vec<u8>);
  44. impl Transaction {
  45. fn hash(&self) -> Result<blake2b_simd::Hash> {
  46. let mut hasher = blake2b_simd::Params::new().hash_length(HASH_LEN).to_state();
  47. self.encode(&mut hasher)?;
  48. Ok(hasher.finalize())
  49. }
  50. }
  51. #[derive(Clone, SerialEncodable, SerialDecodable)]
  52. struct BlockHeader {
  53. nonce: u32,
  54. previous_hash: blake2b_simd::Hash,
  55. timestamp: Timestamp,
  56. txtree: MerkleTree,
  57. }
  58. #[derive(Clone, SerialEncodable, SerialDecodable)]
  59. struct Block {
  60. header: BlockHeader,
  61. transactions: Vec<Transaction>,
  62. }
  63. impl Block {
  64. fn hash(&self) -> Result<blake2b_simd::Hash> {
  65. let mut len = 0;
  66. let mut hasher = blake2b_simd::Params::new().hash_length(HASH_LEN).to_state();
  67. len += self.header.encode(&mut hasher)?;
  68. len += self.header.txtree.root(0).unwrap().encode(&mut hasher)?;
  69. len += self.transactions.len().encode(&mut hasher)?;
  70. len.encode(&mut hasher)?;
  71. Ok(hasher.finalize())
  72. }
  73. fn insert_tx(&mut self, tx: &Transaction) -> Result<()> {
  74. let mut buf = [0u8; 64];
  75. buf[..HASH_LEN].copy_from_slice(tx.hash()?.as_bytes());
  76. let leaf = pallas::Base::from_uniform_bytes(&buf);
  77. self.header.txtree.append(leaf.into());
  78. Ok(())
  79. }
  80. }
  81. fn main() -> Result<()> {
  82. // Construct the genesis block
  83. let genesis_hash =
  84. blake2b_simd::Params::new().hash_length(HASH_LEN).to_state().update(GENESIS).finalize();
  85. let mut genesis_block = Block {
  86. header: BlockHeader {
  87. nonce: 0,
  88. previous_hash: genesis_hash,
  89. timestamp: Timestamp(1693213806),
  90. txtree: MerkleTree::new(100),
  91. },
  92. transactions: vec![],
  93. };
  94. let genesis_tx = Transaction(vec![1, 3, 3, 7]);
  95. genesis_block.insert_tx(&genesis_tx)?;
  96. let mut cur_block = genesis_block;
  97. for i in 0..N_BLOCKS {
  98. // Get the PoW input. The key changes with every mined block.
  99. let pow_input = cur_block.hash()?;
  100. println!("[{}] [MINER] PoW Input: {}", i, pow_input.to_hex());
  101. let miner_setup = Instant::now();
  102. let flags = RandomXFlags::default() | RandomXFlags::FULLMEM;
  103. println!("[{}] [MINER] Initializing RandomX dataset...", i);
  104. let dataset = Arc::new(RandomXDataset::new(flags, pow_input.as_bytes(), 1).unwrap());
  105. // The miner creates a block
  106. let mut miner_block = Block {
  107. header: BlockHeader {
  108. nonce: 0,
  109. previous_hash: cur_block.hash()?,
  110. timestamp: Timestamp::current_time(),
  111. txtree: MerkleTree::new(100),
  112. },
  113. transactions: vec![],
  114. };
  115. let tx0 = Transaction(OsRng.gen::<[u8; 32]>().to_vec());
  116. let tx1 = Transaction(OsRng.gen::<[u8; 32]>().to_vec());
  117. miner_block.insert_tx(&tx0)?;
  118. miner_block.insert_tx(&tx1)?;
  119. println!("[{}] [MINER] Setup time: {:?}", i, miner_setup.elapsed());
  120. // Multithreaded mining setup
  121. let mining_time = Instant::now();
  122. // Let's use 4 threads
  123. const NUM_THREADS: u32 = 4;
  124. let mut handles = vec![];
  125. let found_block = Arc::new(AtomicBool::new(false));
  126. let found_nonce = Arc::new(AtomicU32::new(0));
  127. for t in 0..NUM_THREADS {
  128. let mut block = miner_block.clone();
  129. let found_block = Arc::clone(&found_block);
  130. let found_nonce = Arc::clone(&found_nonce);
  131. let dataset = dataset.clone();
  132. handles.push(thread::spawn(move || {
  133. println!("[{}] [MINER] Initializing RandomX VM #{}...", i, t);
  134. block.header.nonce = t;
  135. let vm = RandomXVM::new_fast(flags, &dataset).unwrap();
  136. loop {
  137. if found_block.load(Ordering::SeqCst) {
  138. println!("[{}] [MINER] Block was found, thread #{} exiting", i, t);
  139. break
  140. }
  141. let out_hash = vm.hash(block.hash().unwrap().as_bytes());
  142. let mut success = true;
  143. for idx in out_hash.iter().take(DIFFICULTY) {
  144. if *idx != 0x00 {
  145. success = false;
  146. }
  147. }
  148. if success {
  149. found_block.store(true, Ordering::SeqCst);
  150. found_nonce.store(block.header.nonce, Ordering::SeqCst);
  151. println!(
  152. "[{}] [MINER] Thread #{} found block using nonce {}",
  153. i, t, block.header.nonce
  154. );
  155. println!("[{}] [MINER] Block hash {}", i, block.hash().unwrap().to_hex(),);
  156. break
  157. }
  158. // This means thread 0 will use nonces, 0, 4, 8, ...
  159. // and thread 1 will use nonces, 1, 5, 9, ...
  160. block.header.nonce += NUM_THREADS;
  161. }
  162. }))
  163. }
  164. // Melt the CPU
  165. for handle in handles {
  166. let _ = handle.join();
  167. }
  168. println!("[{}] [MINER] Mining time: {:?}", i, mining_time.elapsed());
  169. // Set the valid mined nonce in the block that's broadcasted
  170. miner_block.header.nonce = found_nonce.load(Ordering::SeqCst);
  171. // Verify
  172. let verifier_setup = Instant::now();
  173. let flags = RandomXFlags::default();
  174. let cache = RandomXCache::new(flags, pow_input.as_bytes()).unwrap();
  175. let vm = RandomXVM::new(flags, &cache).unwrap();
  176. println!("[{}] [VERIFIER] Setup time: {:?}", i, verifier_setup.elapsed());
  177. let verification_time = Instant::now();
  178. let out_hash = vm.hash(miner_block.hash()?.as_bytes());
  179. for idx in out_hash.iter().take(DIFFICULTY) {
  180. assert!(*idx == 0x00);
  181. }
  182. println!("[{}] [VERIFIER] Verification time: {:?}", i, verification_time.elapsed());
  183. // The new block appends to the blockchain
  184. cur_block = miner_block;
  185. }
  186. Ok(())
  187. }