From e7014dd683cf44bb35788bb58ef7437dbca004ac Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Oct 2024 19:10:49 -0300 Subject: [PATCH 1/3] add nominal_hashrate_multiplier CLI arg to mining-device --- roles/test-utils/mining-device/README.md | 28 ++++++++++---- roles/test-utils/mining-device/src/lib/mod.rs | 37 +++++++++++++++---- roles/test-utils/mining-device/src/main.rs | 9 +++++ 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/roles/test-utils/mining-device/README.md b/roles/test-utils/mining-device/README.md index 4065c2c30..b85bcc6e4 100644 --- a/roles/test-utils/mining-device/README.md +++ b/roles/test-utils/mining-device/README.md @@ -3,16 +3,28 @@ Header only sv2 cpu miner. ``` -Usage: mining-device [OPTIONS] --address-pool +Usage: mining_device [OPTIONS] --address-pool Options: - -p, --pubkey-pool Pool pub key, when left empty the pool certificate is not checked - -i, --id-device Sometimes used by the pool to identify the device - -a, --address-pool Address of the pool in this format ip:port or domain:port - --handicap This value is used to slow down the cpu miner, it represents the number of micro-seconds that are awaited between hashes [default: 0] - --id-user User id, used when a new channel is opened, it can be used by the pool to identify the miner - -h, --help Print help - -V, --version Print version + -p, --pubkey-pool + Pool pub key, when left empty the pool certificate is not checked + -i, --id-device + Sometimes used by the pool to identify the device + -a, --address-pool + Address of the pool in this format ip:port or domain:port + --handicap + This value is used to slow down the cpu miner, it represents the number of micro-seconds that are awaited between hashes [default: 0] + --id-user + User id, used when a new channel is opened, it can be used by the pool to identify the miner + --nominal-hashrate-multiplier + This floating point number is used to modify the advertised nominal hashrate when opening a channel with the upstream. + If 0.0 < nominal_hashrate_multiplier < 1.0, the CPU miner will advertise a nominal hashrate that is smaller than its real capacity. + If nominal_hashrate_multiplier > 1.0, the CPU miner will advertise a nominal hashrate that is bigger than its real capacity. + If empty, the CPU miner will simply advertise its real capacity. + -h, --help + Print help + -V, --version + Print version ``` Usage example: diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index abfa1d969..b1ce7a049 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -42,6 +42,7 @@ pub async fn connect( device_id: Option, user_id: Option, handicap: u32, + nominal_hashrate_multiplier: Option, ) { let address = address .clone() @@ -77,7 +78,16 @@ pub async fn connect( .await .unwrap(); info!("Pool noise connection established at {}", address); - Device::start(receiver, sender, address, device_id, user_id, handicap).await + Device::start( + receiver, + sender, + address, + device_id, + user_id, + handicap, + nominal_hashrate_multiplier, + ) + .await } pub type Message = MiningDeviceMessages<'static>; @@ -193,13 +203,22 @@ pub struct Device { notify_changes_to_mining_thread: NewWorkNotifier, } -fn open_channel(device_id: Option) -> OpenStandardMiningChannel<'static> { +fn open_channel( + device_id: Option, + nominal_hashrate_multiplier: Option, + handicap: u32, +) -> OpenStandardMiningChannel<'static> { let user_identity = device_id.unwrap_or_default().try_into().unwrap(); let id: u32 = 10; info!("Measuring CPU hashrate"); let p = std::thread::available_parallelism().unwrap().get() as u32 - 3; - let nominal_hash_rate = measure_hashrate(5) as f32 * p as f32; - info!("Pc hashrate is {}", nominal_hash_rate); + let measured_hashrate = measure_hashrate(5, handicap) as f32 * p as f32; + info!("Measured CPU hashrate is {}", measured_hashrate); + let nominal_hash_rate = match nominal_hashrate_multiplier { + Some(m) => measured_hashrate * m, + None => measured_hashrate, + }; + info!("MINING DEVICE: send open channel with request id {}", id); OpenStandardMiningChannel { request_id: id.into(), @@ -217,6 +236,7 @@ impl Device { device_id: Option, user_id: Option, handicap: u32, + nominal_hashrate_multiplier: Option, ) { let setup_connection_handler = Arc::new(Mutex::new(SetupConnectionHandler::new())); SetupConnectionHandler::setup( @@ -244,8 +264,9 @@ impl Device { sender: notify_changes_to_mining_thread, }, }; - let open_channel = - MiningDeviceMessages::Mining(Mining::OpenStandardMiningChannel(open_channel(user_id))); + let open_channel = MiningDeviceMessages::Mining(Mining::OpenStandardMiningChannel( + open_channel(user_id, nominal_hashrate_multiplier, handicap), + )); let frame: StdFrame = open_channel.try_into().unwrap(); self_.sender.send(frame.into()).await.unwrap(); let self_mutex = std::sync::Arc::new(Mutex::new(self_)); @@ -597,7 +618,7 @@ impl NextShareOutcome { } // returns hashrate based on how fast the device hashes over the given duration -fn measure_hashrate(duration_secs: u64) -> f64 { +fn measure_hashrate(duration_secs: u64, handicap: u32) -> f64 { let mut rng = thread_rng(); let prev_hash: [u8; 32] = generate_random_32_byte_array().to_vec().try_into().unwrap(); let prev_hash = Hash::from_inner(prev_hash); @@ -619,7 +640,7 @@ fn measure_hashrate(duration_secs: u64) -> f64 { let start_time = Instant::now(); let mut hashes: u64 = 0; let duration = Duration::from_secs(duration_secs); - let mut miner = Miner::new(0); + let mut miner = Miner::new(handicap); // We put the target to 0 we are only interested in how many hashes per unit of time we can do // and do not want to be botherd by messages about valid shares found. miner.new_target(vec![0_u8; 32]); diff --git a/roles/test-utils/mining-device/src/main.rs b/roles/test-utils/mining-device/src/main.rs index 02a19c12f..74bff1b3f 100644 --- a/roles/test-utils/mining-device/src/main.rs +++ b/roles/test-utils/mining-device/src/main.rs @@ -39,6 +39,14 @@ struct Args { help = "User id, used when a new channel is opened, it can be used by the pool to identify the miner" )] id_user: Option, + #[arg( + long, + help = "This floating point number is used to modify the advertised nominal hashrate when opening a channel with the upstream.\ + \nIf 0.0 < nominal_hashrate_multiplier < 1.0, the CPU miner will advertise a nominal hashrate that is smaller than its real capacity.\ + \nIf nominal_hashrate_multiplier > 1.0, the CPU miner will advertise a nominal hashrate that is bigger than its real capacity.\ + \nIf empty, the CPU miner will simply advertise its real capacity." + )] + nominal_hashrate_multiplier: Option, } #[tokio::main(flavor = "current_thread")] @@ -52,6 +60,7 @@ async fn main() { args.id_device, args.id_user, args.handicap, + args.nominal_hashrate_multiplier, ) .await; } From 3b8ade731e710d6659963bc028acf06f0f653910 Mon Sep 17 00:00:00 2001 From: plebhash Date: Tue, 8 Oct 2024 19:48:02 -0300 Subject: [PATCH 2/3] sanitize parallelism --- roles/test-utils/mining-device/src/lib/mod.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index b1ce7a049..af4404c58 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -8,6 +8,7 @@ use std::{ atomic::{AtomicBool, Ordering}, Arc, }, + thread::available_parallelism, time::Duration, }; use tokio::net::TcpStream; @@ -211,8 +212,7 @@ fn open_channel( let user_identity = device_id.unwrap_or_default().try_into().unwrap(); let id: u32 = 10; info!("Measuring CPU hashrate"); - let p = std::thread::available_parallelism().unwrap().get() as u32 - 3; - let measured_hashrate = measure_hashrate(5, handicap) as f32 * p as f32; + let measured_hashrate = measure_hashrate(5, handicap) as f32; info!("Measured CPU hashrate is {}", measured_hashrate); let nominal_hash_rate = match nominal_hashrate_multiplier { Some(m) => measured_hashrate * m, @@ -652,7 +652,12 @@ fn measure_hashrate(duration_secs: u64, handicap: u32) -> f64 { } let elapsed_secs = start_time.elapsed().as_secs_f64(); - hashes as f64 / elapsed_secs + let hashrate_single_thread = hashes as f64 / elapsed_secs; + + // we just measured for a single thread, need to multiply by the available parallelism + let p = available_parallelism().unwrap().get(); + + hashrate_single_thread * p as f64 } fn generate_random_32_byte_array() -> [u8; 32] { let mut rng = thread_rng(); @@ -669,11 +674,7 @@ fn start_mining_threads( tokio::task::spawn(async move { let mut killers: Vec> = vec![]; loop { - let available_parallelism = u32::max( - 2, - std::thread::available_parallelism().unwrap().get() as u32, - ); - let p = available_parallelism - 1; + let p = available_parallelism().unwrap().get() as u32; let unit = u32::MAX / p; while have_new_job.recv().await.is_ok() { while let Some(killer) = killers.pop() { From 1ae28dbdc44879477d8c955eb516f493e8d07565 Mon Sep 17 00:00:00 2001 From: plebhash Date: Wed, 9 Oct 2024 11:55:35 -0300 Subject: [PATCH 3/3] add explanations of handicap and nominal hashrate multiplier --- roles/test-utils/mining-device/README.md | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/roles/test-utils/mining-device/README.md b/roles/test-utils/mining-device/README.md index b85bcc6e4..03ad7241a 100644 --- a/roles/test-utils/mining-device/README.md +++ b/roles/test-utils/mining-device/README.md @@ -31,3 +31,30 @@ Usage example: ``` cargo run --release -- --address-pool 127.0.0.1:20000 --id-device device_id::SOLO::bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh ``` + +## handicap + +CPU mining could damage the system due to excessive heat. + +The `--handicap` parameter should be used as a safety mechanism to slow down the hashrate in order to preserve hardware. + +## nominal hashrate multiplier + +Let's imagine that: +- the upstream wants to receive shares every ~100s (on average) +- the CPU miner nominal hashrate is 1k H/s + +Maybe we want to do a test where we don't want to wait ~100s before a share is submitted by the CPU miner. + +In that case, we need the CPU miner to advertise a smaller hashrate, which will force the upstream to set a lower +difficulty target. + +The `--nominal-hashrate-multiplier` can be used to advertise a custom nominal hashrate. + +In the scenario described above, we could launch the CPU miner with `--nominal-hashrate-multiplier 0.01`. + +The CPU miner would advertise 0.01k H/s, which would cause the upstream to set the difficulty target such that the CPU miner would find a share within ~1s. + +This feature can also be used to advertise a bigger nominal hashrate by using values above `1.0`. + +That can also be useful for testing difficulty adjustment algorithms on Sv2 upstreams. \ No newline at end of file