From ef198a579c4c20908201737b02ad67278df6a885 Mon Sep 17 00:00:00 2001
From: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Date: Thu, 30 Jun 2022 12:46:37 +0200
Subject: [PATCH] staking-miner: CLI flag delay solution x secs (#5734)

* staking-miner: CLI flag delay solution x secs

* Update utils/staking-miner/src/monitor.rs

* Update utils/staking-miner/src/opts.rs

* more logging

* add more verbose logging

* Update utils/staking-miner/src/opts.rs

Co-authored-by: David <dvdplm@gmail.com>

* Update utils/staking-miner/src/opts.rs

Co-authored-by: David <dvdplm@gmail.com>

* remove redundant check

Co-authored-by: David <dvdplm@gmail.com>
---
 polkadot/utils/staking-miner/src/monitor.rs | 41 ++++++++++++++++++---
 polkadot/utils/staking-miner/src/opts.rs    | 14 +++++++
 2 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/polkadot/utils/staking-miner/src/monitor.rs b/polkadot/utils/staking-miner/src/monitor.rs
index 59e9555af63..34e522b5b05 100644
--- a/polkadot/utils/staking-miner/src/monitor.rs
+++ b/polkadot/utils/staking-miner/src/monitor.rs
@@ -87,6 +87,7 @@ async fn ensure_no_better_solution<T: EPM::Config, B: BlockT>(
 	at: Hash,
 	score: sp_npos_elections::ElectionScore,
 	strategy: SubmissionStrategy,
+	max_submissions: u32,
 ) -> Result<(), Error<T>> {
 	let epsilon = match strategy {
 		// don't care about current scores.
@@ -103,14 +104,38 @@ async fn ensure_no_better_solution<T: EPM::Config, B: BlockT>(
 		.map_err::<Error<T>, _>(Into::into)?
 		.unwrap_or_default();
 
+	let mut is_best_score = true;
+	let mut scores = 0;
+
+	log::debug!(target: LOG_TARGET, "submitted solutions on chain: {:?}", indices);
+
 	// BTreeMap is ordered, take last to get the max score.
-	if let Some(curr_max_score) = indices.into_iter().last().map(|(s, _)| s) {
+	for (curr_max_score, _) in indices.into_iter() {
 		if !score.strict_threshold_better(curr_max_score, epsilon) {
-			return Err(Error::StrategyNotSatisfied)
+			log::warn!(target: LOG_TARGET, "mined score is not better; skipping to submit");
+			is_best_score = false;
 		}
+
+		if score == curr_max_score {
+			log::warn!(
+				target: LOG_TARGET,
+				"mined score has the same score as already submitted score"
+			);
+		}
+
+		// Indices can't be bigger than u32::MAX so can't overflow.
+		scores += 1;
 	}
 
-	Ok(())
+	if scores == max_submissions {
+		log::warn!(target: LOG_TARGET, "The submissions queue is full");
+	}
+
+	if is_best_score {
+		Ok(())
+	} else {
+		Err(Error::StrategyNotSatisfied)
+	}
 }
 
 macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
@@ -206,6 +231,8 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
 				ensure_signed_phase::<Runtime, Block>(&rpc1, hash).await
 			});
 
+			tokio::time::sleep(std::time::Duration::from_secs(config.delay as u64)).await;
+
 			let no_prev_sol_fut = tokio::spawn(async move {
 				ensure_no_previous_solution::<Runtime, Block>(&rpc2, hash, &account).await
 			});
@@ -265,7 +292,8 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
 			let rpc2 = rpc.clone();
 
 			let ensure_no_better_fut = tokio::spawn(async move {
-				ensure_no_better_solution::<Runtime, Block>(&rpc1, hash, score, config.submission_strategy).await
+				ensure_no_better_solution::<Runtime, Block>(&rpc1, hash, score, config.submission_strategy,
+					SignedMaxSubmissions::get()).await
 			});
 
 			let ensure_signed_phase_fut = tokio::spawn(async move {
@@ -273,10 +301,11 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
 			});
 
 			// Run the calls in parallel and return once all has completed or any failed.
-			if tokio::try_join!(
+			if let Err(err) = tokio::try_join!(
 				flatten(ensure_no_better_fut),
 				flatten(ensure_signed_phase_fut),
-			).is_err() {
+			) {
+				log::debug!(target: LOG_TARGET, "Skipping to submit at block {}; {}", at.number, err);
 				return;
 			}
 
diff --git a/polkadot/utils/staking-miner/src/opts.rs b/polkadot/utils/staking-miner/src/opts.rs
index 63425b89a10..1e7b1f2ba29 100644
--- a/polkadot/utils/staking-miner/src/opts.rs
+++ b/polkadot/utils/staking-miner/src/opts.rs
@@ -83,6 +83,17 @@ pub(crate) struct MonitorConfig {
 	/// `--submission-strategy "percent-better <percent>"`: submit if the submission is `n` percent better.
 	#[clap(long, parse(try_from_str), default_value = "if-leading")]
 	pub submission_strategy: SubmissionStrategy,
+
+	/// Delay in number seconds to wait until starting mining a solution.
+	///
+	/// At every block when a solution is attempted
+	/// a delay can be enforced to avoid submitting at
+	/// "same time" and risk potential races with other miners.
+	///
+	/// When this is enabled and there are competing solutions, your solution might not be submitted
+	/// if the scores are equal.
+	#[clap(long, parse(try_from_str), default_value_t = 0)]
+	pub delay: usize,
 }
 
 #[derive(Debug, Clone, Parser)]
@@ -202,6 +213,8 @@ mod test_super {
 			"//Alice",
 			"--listen",
 			"head",
+			"--delay",
+			"12",
 			"seq-phragmen",
 		])
 		.unwrap();
@@ -215,6 +228,7 @@ mod test_super {
 					listen: "head".to_string(),
 					solver: Solver::SeqPhragmen { iterations: 10 },
 					submission_strategy: SubmissionStrategy::IfLeading,
+					delay: 12,
 				}),
 			}
 		);
-- 
GitLab