| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- //! randomx example that mines a dummy header using multiple threads
- use std::{
- collections::VecDeque,
- sync::{
- atomic::{AtomicBool, AtomicU64, Ordering},
- Arc,
- },
- thread,
- time::Instant,
- };
- use num_bigint::BigUint;
- use randomx::*;
- #[derive(Clone, Debug)]
- struct Header {
- version: u8,
- previous: blake3::Hash,
- height: u32,
- nonce: u64,
- }
- impl Header {
- fn new(previous: blake3::Hash) -> Self {
- Self {
- version: 1,
- previous,
- height: 1,
- nonce: 0,
- }
- }
- fn hash(&self) -> blake3::Hash {
- let mut hasher = blake3::Hasher::new();
- hasher.update(&self.version.to_le_bytes());
- hasher.update(self.previous.as_bytes());
- hasher.update(&self.height.to_le_bytes());
- hasher.update(&self.nonce.to_le_bytes());
- hasher.finalize()
- }
- }
- fn get_mining_flags() -> RandomXFlags {
- // Try adding `| RandomXFlags::LARGEPAGES`.
- RandomXFlags::get_recommended_flags() | RandomXFlags::FULLMEM
- }
- fn single_thread_mine(header: &mut Header, target: &BigUint, input: &[u8; 32]) {
- println!("Initializing RandomX cache and dataset...");
- let setup_start = Instant::now();
- let flags = get_mining_flags();
- let cache = RandomXCache::new(flags, &input[..]).unwrap();
- let dataset_item_count = RandomXDataset::count().unwrap();
- let dataset = RandomXDataset::new_init(flags, cache, 0, dataset_item_count).unwrap();
- println!("Setup time: {:?}", setup_start.elapsed());
- println!("Initializing RandomX VM...");
- let vm_start = Instant::now();
- let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
- println!("Initialized RandomX VM in {:?}", vm_start.elapsed());
- println!("Mining started!");
- let mining_start = Instant::now();
- loop {
- let out_hash = vm.calculate_hash(header.hash().as_bytes()).unwrap();
- let out_hash = BigUint::from_bytes_le(&out_hash);
- if &out_hash <= target {
- println!("Found block header using nonce {}", header.nonce);
- println!("Block header hash {}", header.hash());
- println!("RandomX output: 0x{out_hash:064x}");
- break;
- }
- header.nonce += 1;
- }
- println!("Completed mining in {:?}", mining_start.elapsed());
- println!("Mined header: {header:?}");
- }
- fn multi_thread_mine(threads: usize, header: &mut Header, target: &BigUint, input: &[u8; 32]) {
- println!("Initializing RandomX cache and dataset...");
- let setup_start = Instant::now();
- let flags = get_mining_flags();
- let cache = RandomXCache::new(flags, &input[..]).unwrap();
- let dataset_item_count = RandomXDataset::count().unwrap();
- let dataset = RandomXDataset::new(flags, cache, dataset_item_count).unwrap();
- let mut subsets = VecDeque::with_capacity(threads);
- // Multithreaded dataset init
- let threads_u32 = threads as u32;
- for t in 0..threads_u32 {
- println!("Initializing RandomX dataset for thread #{t}...");
- let ds_start = Instant::now();
- let a = (dataset_item_count * t) / threads_u32;
- let b = (dataset_item_count * (t + 1)) / threads_u32;
- subsets.push_back(dataset.subset_init(a, b - a));
- println!(
- "Initialized RandomX dataset for thread #{t} in {:?}",
- ds_start.elapsed()
- );
- }
- println!("Setup time: {:?}", setup_start.elapsed());
- println!("Initializing mining threads...");
- let mut handles = Vec::with_capacity(threads);
- let found_header = Arc::new(AtomicBool::new(false));
- let found_nonce = Arc::new(AtomicU64::new(0));
- let threads_u64 = threads as u64;
- let mining_start = Instant::now();
- for t in 0..threads_u64 {
- if found_header.load(Ordering::SeqCst) {
- println!("Block header found, threads creation loop exiting");
- break;
- }
- let target = target.clone();
- let mut thread_header = header.clone();
- thread_header.nonce = t;
- let found_header = Arc::clone(&found_header);
- let found_nonce = Arc::clone(&found_nonce);
- let dataset = subsets.pop_front().unwrap();
- handles.push(thread::spawn(move || {
- println!("Initializing RandomX VM #{t}...");
- let vm_start = Instant::now();
- let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
- println!("Initialized RandomX VM #{t} in {:?}", vm_start.elapsed());
- loop {
- if found_header.load(Ordering::SeqCst) {
- println!("Block header found, thread #{t} exiting");
- break;
- }
- let out_hash = vm.calculate_hash(thread_header.hash().as_bytes()).unwrap();
- let out_hash = BigUint::from_bytes_le(&out_hash);
- if out_hash <= target {
- found_header.store(true, Ordering::SeqCst);
- found_nonce.store(thread_header.nonce, Ordering::SeqCst);
- println!(
- "Thread #{t} found block header using nonce {}",
- thread_header.nonce
- );
- println!("Block header hash {}", thread_header.hash());
- println!("RandomX output: 0x{out_hash:064x}");
- break;
- }
- // This means thread 0 will use nonces, 0, 4, 8, ...
- // and thread 1 will use nonces, 1, 5, 9, ...
- thread_header.nonce += threads_u64;
- }
- }));
- }
- // Wait for threads to finish mining
- for handle in handles {
- let _ = handle.join();
- }
- println!("Completed mining in {:?}", mining_start.elapsed());
- header.nonce = found_nonce.load(Ordering::SeqCst);
- println!("Mined header: {header:?}");
- }
- fn main() {
- const THREADS: usize = 8;
- let difficulty = BigUint::from(2500_u32);
- let target = BigUint::from_bytes_le(&[0xFF; 32]) / difficulty;
- let previous = blake3::hash(b"Let there be dark!");
- let input = previous.as_bytes();
- let mut header = Header::new(previous);
- println!("Mine target: 0x{target:064x}");
- println!("PoW input: {previous}");
- match THREADS {
- 0 => panic!("Can't use 0 threads!"),
- 1 => single_thread_mine(&mut header, &target, input),
- _ => multi_thread_mine(THREADS, &mut header, &target, input),
- }
- println!("Initializing verifier...");
- let verifier_setup = Instant::now();
- let flags = RandomXFlags::get_recommended_flags();
- let cache = RandomXCache::new(flags, input).unwrap();
- let vm = RandomXVM::new(flags, Some(cache), None).unwrap();
- println!("Setup time: {:?}", verifier_setup.elapsed());
- let hashing_time = Instant::now();
- let out_hash = vm.calculate_hash(header.hash().as_bytes()).unwrap();
- println!("Completed hashing in {:?}", hashing_time.elapsed());
- let out_hash = BigUint::from_bytes_le(&out_hash);
- println!("RandomX output: 0x{out_hash:064x}");
- assert!(out_hash <= target);
- }
|