diff --git a/grin.toml b/grin.toml index 193ebff9d..0c713e14b 100644 --- a/grin.toml +++ b/grin.toml @@ -124,6 +124,9 @@ stratum_server_addr = "127.0.0.1:13416" #header before stopping and re-collecting transactions from the pool attempt_time_per_block = 90 +#The minimum acceptable share difficulty to request from miners +minimum_share_difficulty = 1 + #the wallet reciever to which coinbase rewards will be sent wallet_listener_url = "http://127.0.0.1:13415" diff --git a/servers/src/common/types.rs b/servers/src/common/types.rs index b78ff3e9e..218a384bd 100644 --- a/servers/src/common/types.rs +++ b/servers/src/common/types.rs @@ -227,6 +227,9 @@ pub struct StratumServerConfig { /// and starting again pub attempt_time_per_block: u32, + /// Minimum difficulty for worker shares + pub minimum_share_difficulty: u64, + /// Base address to the HTTP wallet receiver pub wallet_listener_url: String, @@ -240,7 +243,8 @@ impl Default for StratumServerConfig { StratumServerConfig { wallet_listener_url: "http://localhost:13415".to_string(), burn_reward: false, - attempt_time_per_block: 2, + attempt_time_per_block: ::max_value(), + minimum_share_difficulty: 1, enable_stratum_server: None, stratum_server_addr: None, } diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 1801cd91f..bac7d512c 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -279,7 +279,6 @@ impl Server { .name("stratum_server".to_string()) .spawn(move || { stratum_server.run_loop( - config.clone(), stratum_stats, cuckoo_size as u32, proof_size, @@ -304,6 +303,7 @@ impl Server { enable_stratum_server: None, stratum_server_addr: None, wallet_listener_url: config_wallet_url, + minimum_share_difficulty: 1, }; let mut miner = Miner::new( diff --git a/servers/src/mining/stratumserver.rs b/servers/src/mining/stratumserver.rs index b7f64fa3c..4e3f02a3b 100644 --- a/servers/src/mining/stratumserver.rs +++ b/servers/src/mining/stratumserver.rs @@ -15,6 +15,7 @@ //! Mining Stratum Server use bufstream::BufStream; use serde_json; +use std::cmp; use std::error::Error; use std::io::BufRead; use std::io::{ErrorKind, Write}; @@ -25,7 +26,6 @@ use std::thread; use std::time::Duration; use std::time::SystemTime; use time; -use util::LOGGER; use chain; use common::adapters::PoolToChainAdapter; @@ -35,6 +35,7 @@ use core::core::{Block, BlockHeader}; use keychain; use mining::mine_block; use pool; +use util::LOGGER; // Max number of transactions this miner will assemble in a block const MAX_TX: u32 = 5000; @@ -231,6 +232,7 @@ pub struct StratumServer { tx_pool: Arc>>, current_block: Block, current_difficulty: u64, + minimum_share_difficulty: u64, current_key_id: Option, workers: Arc>>, currently_syncing: Arc, @@ -245,6 +247,7 @@ impl StratumServer { ) -> StratumServer { StratumServer { id: String::from("StratumServer"), + minimum_share_difficulty: config.minimum_share_difficulty, config: config, chain: chain_ref, tx_pool: tx_pool, @@ -264,7 +267,7 @@ impl StratumServer { let pre = pre_pow_writer.as_hex_string(false); let job_template = JobTemplate { height: bh.height, - difficulty: self.current_difficulty, + difficulty: self.minimum_share_difficulty, pre_pow: pre, }; return job_template; @@ -421,7 +424,9 @@ impl StratumServer { // Handle SUBMIT message // params contains a solved block header - // we are expecting real solutions at the full difficulty. + // We accept and log valid shares of all difficulty above configured minimum + // Accepted shares that are full solutions will also be submitted to the + // network fn handle_submit( &self, params: Option, @@ -442,26 +447,69 @@ impl StratumServer { }; let mut b: Block; + let share_difficulty: u64; if submit_params.height == self.current_block.header.height { // Reconstruct the block header with this nonce and pow added b = self.current_block.clone(); b.header.nonce = submit_params.nonce; b.header.pow.nonces = submit_params.pow; - let res = self.chain.process_block(b.clone(), chain::Options::MINE); - if let Err(e) = res { + // Get share difficulty + share_difficulty = b.header.pow.to_difficulty().to_num(); + // If the difficulty is too low its an error + if share_difficulty < self.minimum_share_difficulty { + // Return error status error!( LOGGER, - "(Server ID: {}) Error validating mined block: {:?}", self.id, e + "(Server ID: {}) Share rejected due to low difficulty: {}/{}", + self.id, + share_difficulty, + self.minimum_share_difficulty, ); worker_stats.num_rejected += 1; - let e = r#"{"code": -1, "message": "Solution validation failed"}"#; + let e = r#"{"code": -1, "message": "Share rejected due to low difficulty"}"#; let err = e.to_string(); return (err, true); } + // If the difficulty is high enough, submit it (which also validates it) + if share_difficulty >= self.current_difficulty { + let res = self.chain.process_block(b.clone(), chain::Options::MINE); + if let Err(e) = res { + // Return error status + error!( + LOGGER, + "(Server ID: {}) Failed to validate solution at height {}: {:?}", + self.id, + submit_params.height, + e + ); + worker_stats.num_rejected += 1; + let e = r#"{"code": -1, "message": "Failed to validate solution"}"#; + let err = e.to_string(); + return (err, true); + } + // Success case falls through to be logged + } else { + // Do some validation but dont submit + if self.current_block.header.pre_pow_hash() != b.header.pre_pow_hash() { + // Return error status + error!( + LOGGER, + "(Server ID: {}) Failed to validate share at height {} with nonce {}", + self.id, + submit_params.height, + b.header.nonce + ); + worker_stats.num_rejected += 1; + let e = r#"{"code": -1, "message": "Failed to validate share"}"#; + let err = e.to_string(); + return (err, true); + } + } } else { - warn!( + // Return error status + error!( LOGGER, - "(Server ID: {}) Found POW for block at height: {} - but too late", + "(Server ID: {}) Share at height {} submitted too late", self.id, submit_params.height ); @@ -470,17 +518,20 @@ impl StratumServer { let err = e.to_string(); return (err, true); } + // Log this as a valid share let submitted_by = match worker.login.clone() { None => worker.id.to_string(), Some(login) => login.clone(), }; info!( LOGGER, - "(Server ID: {}) Found POW for block with hash {} at height {} using nonce {} submitted by worker {}", + "(Server ID: {}) Got share for block: hash {}, height {}, nonce {}, difficulty {}/{}, submitted by {}", self.id, b.hash(), b.header.height, b.header.nonce, + share_difficulty, + self.current_difficulty, submitted_by, ); worker_stats.num_accepted += 1; @@ -544,6 +595,8 @@ impl StratumServer { let job_request_json = serde_json::to_string(&job_request).unwrap(); // Push the new block to all connected clients + // NOTE: We do not give a uniqe nonce (should we?) so miners need + // to choose one for themselves let mut workers_l = self.workers.lock().unwrap(); for num in 0..workers_l.len() { workers_l[num].write_message(job_request_json.clone()); @@ -557,7 +610,6 @@ impl StratumServer { /// be submitted. pub fn run_loop( &mut self, - miner_config: StratumServerConfig, stratum_stats: Arc>, cuckoo_size: u32, proof_size: usize, @@ -574,7 +626,7 @@ impl StratumServer { self.currently_syncing = currently_syncing; // "globals" for this function - let attempt_time_per_block = miner_config.attempt_time_per_block; + let attempt_time_per_block = self.config.attempt_time_per_block; let mut deadline: i64 = 0; // to prevent the wallet from generating a new HD key derivation for each // iteration, we keep the returned derivation to provide it back when @@ -584,7 +636,7 @@ impl StratumServer { let mut head = self.chain.head().unwrap(); let mut current_hash = head.prev_block_h; let mut latest_hash; - let listen_addr = miner_config.stratum_server_addr.clone().unwrap(); + let listen_addr = self.config.stratum_server_addr.clone().unwrap(); // Start a thread to accept new worker connections let mut workers_th = self.workers.clone(); @@ -604,7 +656,7 @@ impl StratumServer { warn!( LOGGER, "Stratum server started on {}", - miner_config.stratum_server_addr.unwrap() + self.config.stratum_server_addr.clone().unwrap() ); // Main Loop @@ -646,6 +698,11 @@ impl StratumServer { .to_num(); self.current_key_id = block_fees.key_id(); current_hash = latest_hash; + // set the minimum acceptable share difficulty for this block + self.minimum_share_difficulty = cmp::min( + self.config.minimum_share_difficulty, + self.current_difficulty, + ); // set a new deadline for rebuilding with fresh transactions deadline = time::get_time().sec + attempt_time_per_block as i64; diff --git a/servers/tests/framework/mod.rs b/servers/tests/framework/mod.rs index b0f1b6bb7..806c26bcc 100644 --- a/servers/tests/framework/mod.rs +++ b/servers/tests/framework/mod.rs @@ -579,6 +579,7 @@ pub fn stratum_config() -> servers::common::types::StratumServerConfig { enable_stratum_server: Some(true), stratum_server_addr: Some(String::from("127.0.0.1:13416")), attempt_time_per_block: 60, + minimum_share_difficulty: 1, wallet_listener_url: String::from("http://127.0.0.1:13415"), burn_reward: false, }