mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
add job_id to stratum job and solution rpc messages (#1240)
* add job_id to stratum job and solution rpc messages * remove unnecessary clone
This commit is contained in:
parent
64db4d92f0
commit
c7d78446f3
1 changed files with 125 additions and 92 deletions
|
@ -28,7 +28,7 @@ use chain;
|
|||
use common::adapters::PoolToChainAdapter;
|
||||
use common::stats::{StratumStats, WorkerStats};
|
||||
use common::types::{StratumServerConfig, SyncState};
|
||||
use core::core::{Block, BlockHeader};
|
||||
use core::core::Block;
|
||||
use core::{pow, global};
|
||||
use keychain;
|
||||
use mining::mine_block;
|
||||
|
@ -75,6 +75,7 @@ struct LoginParams {
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct SubmitParams {
|
||||
height: u64,
|
||||
job_id: u64,
|
||||
nonce: u64,
|
||||
pow: Vec<u64>,
|
||||
}
|
||||
|
@ -82,6 +83,7 @@ struct SubmitParams {
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct JobTemplate {
|
||||
height: u64,
|
||||
job_id: u64,
|
||||
difficulty: u64,
|
||||
pre_pow: String,
|
||||
}
|
||||
|
@ -230,7 +232,7 @@ pub struct StratumServer {
|
|||
config: StratumServerConfig,
|
||||
chain: Arc<chain::Chain>,
|
||||
tx_pool: Arc<RwLock<pool::TransactionPool<PoolToChainAdapter>>>,
|
||||
current_block: Block,
|
||||
current_block_versions: Vec<Block>,
|
||||
current_difficulty: u64,
|
||||
minimum_share_difficulty: u64,
|
||||
current_key_id: Option<keychain::Identifier>,
|
||||
|
@ -251,7 +253,7 @@ impl StratumServer {
|
|||
config: config,
|
||||
chain: chain_ref,
|
||||
tx_pool: tx_pool,
|
||||
current_block: Block::default(),
|
||||
current_block_versions: Vec::new(),
|
||||
current_difficulty: <u64>::max_value(),
|
||||
current_key_id: None,
|
||||
workers: Arc::new(Mutex::new(Vec::new())),
|
||||
|
@ -260,13 +262,15 @@ impl StratumServer {
|
|||
}
|
||||
|
||||
// Build and return a JobTemplate for mining the current block
|
||||
fn build_block_template(&self, bh: BlockHeader) -> JobTemplate {
|
||||
fn build_block_template(&self) -> JobTemplate {
|
||||
let bh = self.current_block_versions.last().unwrap().header.clone();
|
||||
// Serialize the block header into pre and post nonce strings
|
||||
let mut pre_pow_writer = mine_block::HeaderPrePowWriter::default();
|
||||
bh.write_pre_pow(&mut pre_pow_writer).unwrap();
|
||||
let pre = pre_pow_writer.as_hex_string(false);
|
||||
let job_template = JobTemplate {
|
||||
height: bh.height,
|
||||
job_id: (self.current_block_versions.len() - 1) as u64,
|
||||
difficulty: self.minimum_share_difficulty,
|
||||
pre_pow: pre,
|
||||
};
|
||||
|
@ -328,8 +332,7 @@ impl StratumServer {
|
|||
};
|
||||
Err(serde_json::to_value(e).unwrap())
|
||||
} else {
|
||||
let b = self.current_block.header.clone();
|
||||
self.handle_getjobtemplate(b)
|
||||
self.handle_getjobtemplate()
|
||||
}
|
||||
}
|
||||
"status" => {
|
||||
|
@ -383,7 +386,7 @@ impl StratumServer {
|
|||
// Return worker status in json for use by a dashboard or healthcheck.
|
||||
let status = WorkerStatus {
|
||||
id: worker_stats.id.clone(),
|
||||
height: self.current_block.header.height,
|
||||
height: self.current_block_versions.last().unwrap().header.height,
|
||||
difficulty: worker_stats.pow_difficulty,
|
||||
accepted: worker_stats.num_accepted,
|
||||
rejected: worker_stats.num_rejected,
|
||||
|
@ -394,10 +397,17 @@ impl StratumServer {
|
|||
}
|
||||
|
||||
// Handle GETJOBTEMPLATE message
|
||||
fn handle_getjobtemplate(&self, bh: BlockHeader) -> Result<Value, Value> {
|
||||
fn handle_getjobtemplate(&self) -> Result<Value, Value> {
|
||||
// Build a JobTemplate from a BlockHeader and return JSON
|
||||
let job_template = self.build_block_template(bh);
|
||||
let job_template = self.build_block_template();
|
||||
let response = serde_json::to_value(&job_template).unwrap();
|
||||
debug!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) sending block {} with id {} to single worker",
|
||||
self.id,
|
||||
job_template.height,
|
||||
job_template.job_id,
|
||||
);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
|
@ -447,77 +457,9 @@ impl StratumServer {
|
|||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
};
|
||||
|
||||
let mut b: Block;
|
||||
let share_difficulty: u64;
|
||||
let mut share_is_block = false;
|
||||
if 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 = params.nonce;
|
||||
b.header.pow.nonces = params.pow;
|
||||
// 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: {}) Share rejected due to low difficulty: {}/{}",
|
||||
self.id,
|
||||
share_difficulty,
|
||||
self.minimum_share_difficulty,
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32501,
|
||||
message: "Share rejected due to low difficulty".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
// 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,
|
||||
params.height,
|
||||
e
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32502,
|
||||
message: "Failed to validate solution".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
} else {
|
||||
share_is_block = true;
|
||||
}
|
||||
// Success case falls through to be logged
|
||||
} else {
|
||||
// This is a low-difficulty share, not a full solution
|
||||
// Do some validation but dont submit
|
||||
if !pow::verify_size(&b.header, global::min_sizeshift()) {
|
||||
// Return error status
|
||||
error!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) Failed to validate share at height {} with nonce {}",
|
||||
self.id,
|
||||
params.height,
|
||||
b.header.nonce
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32502,
|
||||
message: "Failed to validate solution".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if params.height != self.current_block_versions.last().unwrap().header.height {
|
||||
// Return error status
|
||||
error!(
|
||||
LOGGER,
|
||||
|
@ -530,6 +472,93 @@ impl StratumServer {
|
|||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
// Find the correct version of the block to match this header
|
||||
let b: Option<&Block> = self.current_block_versions.get(params.job_id as usize);
|
||||
if b.is_none() {
|
||||
// Return error status
|
||||
error!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) Failed to validate solution at height {}: invalid job_id {}",
|
||||
self.id,
|
||||
params.height,
|
||||
params.job_id,
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32502,
|
||||
message: "Failed to validate solution".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
let mut b: Block = b.unwrap().clone();
|
||||
// Reconstruct the block header with this nonce and pow added
|
||||
b.header.nonce = params.nonce;
|
||||
b.header.pow.nonces = params.pow;
|
||||
// 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: {}) Share rejected due to low difficulty: {}/{}",
|
||||
self.id,
|
||||
share_difficulty,
|
||||
self.minimum_share_difficulty,
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32501,
|
||||
message: "Share rejected due to low difficulty".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
// If the difficulty is high enough, submit it (which also validates it)
|
||||
if share_difficulty >= self.current_difficulty {
|
||||
// This is a full solution, submit it to the network
|
||||
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,
|
||||
params.height,
|
||||
e
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32502,
|
||||
message: "Failed to validate solution".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
share_is_block = true;
|
||||
// Log message to make it obvious we found a block
|
||||
warn!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) Solution Found for block {} - Yay!!!", self.id, params.height
|
||||
);
|
||||
} else {
|
||||
// Do some validation but dont submit
|
||||
if !pow::verify_size(&b.header, global::min_sizeshift()) {
|
||||
// Return error status
|
||||
error!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) Failed to validate share at height {} with nonce {} using job_id {}",
|
||||
self.id,
|
||||
params.height,
|
||||
b.header.nonce,
|
||||
params.job_id,
|
||||
);
|
||||
worker_stats.num_rejected += 1;
|
||||
let e = RpcError {
|
||||
code: -32502,
|
||||
message: "Failed to validate solution".to_string(),
|
||||
};
|
||||
return Err(serde_json::to_value(e).unwrap());
|
||||
}
|
||||
}
|
||||
// Log this as a valid share
|
||||
let submitted_by = match worker.login.clone() {
|
||||
None => worker.id.to_string(),
|
||||
|
@ -588,15 +617,8 @@ impl StratumServer {
|
|||
// Broadcast a jobtemplate RpcRequest to all connected workers - no response
|
||||
// expected
|
||||
fn broadcast_job(&mut self) {
|
||||
debug!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) sending block {} to stratum clients",
|
||||
self.id,
|
||||
self.current_block.header.height
|
||||
);
|
||||
|
||||
// Package new block into RpcRequest
|
||||
let job_template = self.build_block_template(self.current_block.header.clone());
|
||||
let job_template = self.build_block_template();
|
||||
let job_template_json = serde_json::to_string(&job_template).unwrap();
|
||||
// Issue #1159 - use a serde_json Value type to avoid extra quoting
|
||||
let job_template_value: Value = serde_json::from_str(&job_template_json).unwrap();
|
||||
|
@ -607,7 +629,13 @@ impl StratumServer {
|
|||
params: Some(job_template_value),
|
||||
};
|
||||
let job_request_json = serde_json::to_string(&job_request).unwrap();
|
||||
|
||||
debug!(
|
||||
LOGGER,
|
||||
"(Server ID: {}) sending block {} with id {} to stratum clients",
|
||||
self.id,
|
||||
job_template.height,
|
||||
job_template.job_id,
|
||||
);
|
||||
// Push the new block to all connected clients
|
||||
// NOTE: We do not give a unique nonce (should we?) so miners need
|
||||
// to choose one for themselves
|
||||
|
@ -651,6 +679,7 @@ impl StratumServer {
|
|||
let mut current_hash = head.prev_block_h;
|
||||
let mut latest_hash;
|
||||
let listen_addr = self.config.stratum_server_addr.clone().unwrap();
|
||||
self.current_block_versions.push(Block::default());
|
||||
|
||||
// Start a thread to accept new worker connections
|
||||
let mut workers_th = self.workers.clone();
|
||||
|
@ -698,7 +727,11 @@ impl StratumServer {
|
|||
if !self.config.burn_reward {
|
||||
wallet_listener_url = Some(self.config.wallet_listener_url.clone());
|
||||
}
|
||||
|
||||
// If this is a new block, clear the current_block version history
|
||||
if current_hash != latest_hash {
|
||||
self.current_block_versions.clear();
|
||||
}
|
||||
// Build the new block (version)
|
||||
let (new_block, block_fees) = mine_block::get_block(
|
||||
&self.chain,
|
||||
&self.tx_pool,
|
||||
|
@ -706,8 +739,7 @@ impl StratumServer {
|
|||
MAX_TX.clone(),
|
||||
wallet_listener_url,
|
||||
);
|
||||
self.current_block = new_block;
|
||||
self.current_difficulty = (self.current_block.header.total_difficulty.clone()
|
||||
self.current_difficulty = (new_block.header.total_difficulty.clone()
|
||||
- head.total_difficulty.clone())
|
||||
.to_num();
|
||||
self.current_key_id = block_fees.key_id();
|
||||
|
@ -722,10 +754,11 @@ impl StratumServer {
|
|||
|
||||
{
|
||||
let mut stratum_stats = stratum_stats.write().unwrap();
|
||||
stratum_stats.block_height = self.current_block.header.height;
|
||||
stratum_stats.block_height = new_block.header.height;
|
||||
stratum_stats.network_difficulty = self.current_difficulty;
|
||||
}
|
||||
|
||||
// Add this new block version to our current block map
|
||||
self.current_block_versions.push(new_block);
|
||||
// Send this job to all connected workers
|
||||
self.broadcast_job();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue