Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 34 additions & 27 deletions src/dry_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,21 @@ where
+ 'static,
T::Solution: Send,
{
let signer = Signer::new(&config.seed_or_path)?;

let account_info = api
.storage()
.at(None)
.await?
.fetch(&runtime::storage().system().account(signer.account_id()))
.await?
.ok_or(Error::AccountDoesNotExists)?;

log::info!(target: LOG_TARGET, "Loaded account {}, {:?}", signer, account_info);

let round = api
.storage()
.at(config.at)
.await?
.fetch_or_default(&runtime::storage().election_provider_multi_phase().round())
.await?;

let (solution, score, _size) =
epm::fetch_snapshot_and_mine_solution::<T>(&api, config.at, config.solver, round).await?;
let (solution, score, _size) = epm::fetch_snapshot_and_mine_solution::<T>(
&api,
config.at,
config.solver,
round,
config.force_winner_count,
)
.await?;

let round = api
.storage()
Expand All @@ -60,7 +54,6 @@ where
.unwrap_or(1);

let raw_solution = RawSolution { solution, score, round };
let nonce = api.rpc().system_account_next_index(signer.account_id()).await?;

log::info!(
target: LOG_TARGET,
Expand All @@ -69,17 +62,31 @@ where
raw_solution.encode().len(),
);

let tx = epm::signed_solution(raw_solution)?;
let xt = api
.tx()
.create_signed_with_nonce(&tx, &*signer, nonce, ExtrinsicParams::default())?;

let outcome = api.rpc().dry_run(xt.encoded(), config.at).await?;

log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome);

match outcome {
Ok(()) => Ok(()),
Err(e) => Err(Error::Other(format!("{e:?}"))),
// If an account seed or path is provided, then do a dry run to the node. Otherwise,
// we've logged the solution above and we do nothing else.
if let Some(seed_or_path) = &config.seed_or_path {
let signer = Signer::new(seed_or_path)?;
let account_info = api
.storage()
.at(None)
.await?
.fetch(&runtime::storage().system().account(signer.account_id()))
.await?
.ok_or(Error::AccountDoesNotExists)?;

log::info!(target: LOG_TARGET, "Loaded account {}, {:?}", signer, account_info);

let nonce = api.rpc().system_account_next_index(signer.account_id()).await?;
let tx = epm::signed_solution(raw_solution)?;
let xt =
api.tx()
.create_signed_with_nonce(&tx, &*signer, nonce, ExtrinsicParams::default())?;
let outcome = api.rpc().dry_run(xt.encoded(), config.at).await?;

log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome);

outcome.map_err(|e| Error::Other(format!("{e:?}")))?;
}

Ok(())
}
19 changes: 12 additions & 7 deletions src/epm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ pub async fn fetch_snapshot_and_mine_solution<T>(
hash: Option<Hash>,
solver: Solver,
round: u32,
forced_desired_targets: Option<u32>,
) -> Result<(SolutionOf<T>, ElectionScore, SolutionOrSnapshotSize), Error>
where
T: MinerConfig<AccountId = AccountId, MaxVotesPerVoter = static_types::MaxVotesPerVoter>
Expand All @@ -178,13 +179,17 @@ where
T::Solution: Send,
{
let snapshot = snapshot_at(hash, &api).await?;
let desired_targets = api
.storage()
.at(hash)
.await?
.fetch(&runtime::storage().election_provider_multi_phase().desired_targets())
.await?
.expect("Snapshot is non-empty; `desired_target` should exist; qed");

let desired_targets = match forced_desired_targets {
Some(x) => x,
None => api
.storage()
.at(hash)
.await?
.fetch(&runtime::storage().election_provider_multi_phase().desired_targets())
.await?
.expect("Snapshot is non-empty; `desired_target` should exist; qed"),
};

let minimum_untrusted_score = api
.storage()
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ mod tests {
at: None,
solver: Solver::PhragMMS { iterations: 10 },
force_snapshot: false,
seed_or_path: "//Alice".to_string(),
force_winner_count: None,
seed_or_path: Some("//Alice".to_string()),
}),
}
);
Expand Down
55 changes: 30 additions & 25 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,23 +181,28 @@ where
tokio::time::sleep(std::time::Duration::from_secs(config.delay as u64)).await;
let _lock = submit_lock.lock().await;

let (solution, score) =
match epm::fetch_snapshot_and_mine_solution::<T>(&api, Some(hash), config.solver, round)
.timed()
.await
{
(Ok((solution, score, size)), elapsed) => {
let elapsed_ms = elapsed.as_millis();
let encoded_len = solution.encoded_size();
let active_voters = solution.voter_count() as u32;
let desired_targets = solution.unique_targets().len() as u32;

let final_weight = tokio::task::spawn_blocking(move || {
T::solution_weight(size.voters, size.targets, active_voters, desired_targets)
})
.await?;

log::info!(
let (solution, score) = match epm::fetch_snapshot_and_mine_solution::<T>(
&api,
Some(hash),
config.solver,
round,
None,
)
.timed()
.await
{
(Ok((solution, score, size)), elapsed) => {
let elapsed_ms = elapsed.as_millis();
let encoded_len = solution.encoded_size();
let active_voters = solution.voter_count() as u32;
let desired_targets = solution.unique_targets().len() as u32;

let final_weight = tokio::task::spawn_blocking(move || {
T::solution_weight(size.voters, size.targets, active_voters, desired_targets)
})
.await?;

log::info!(
target: LOG_TARGET,
"Mined solution with {:?} size: {:?} round: {:?} at: {}, took: {} ms, len: {:?}, weight = {:?}",
score,
Expand All @@ -209,15 +214,15 @@ where
final_weight,
);

prometheus::set_length(encoded_len);
prometheus::set_weight(final_weight);
prometheus::observe_mined_solution_duration(elapsed_ms as f64);
prometheus::set_score(score);
prometheus::set_length(encoded_len);
prometheus::set_weight(final_weight);
prometheus::observe_mined_solution_duration(elapsed_ms as f64);
prometheus::set_score(score);

(solution, score)
},
(Err(e), _) => return Err(e),
};
(solution, score)
},
(Err(e), _) => return Err(e),
};

let best_head = get_latest_head(&api, config.listen).await?;

Expand Down
10 changes: 8 additions & 2 deletions src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,21 @@ pub struct DryRunConfig {
#[clap(long)]
pub force_snapshot: bool,

/// The number of winners to take, instead of the `desired_targets` in snapshot.
// Doing this would cause the dry-run to typically fail, but that's fine, the program should
// still print out some score, and that should be it.
#[clap(long)]
pub force_winner_count: Option<u32>,

/// The path to a file containing the seed of the account. If the file is not found, the seed is
/// used as-is.
/// used as-is. If this is not provided, we won't attempt to submit anything.
///
/// Can also be provided via the `SEED` environment variable.
///
/// WARNING: Don't use an account with a large stash for this. Based on how the bot is
/// configured, it might re-try and lose funds through transaction fees/deposits.
#[clap(long, short, env = "SEED")]
pub seed_or_path: String,
pub seed_or_path: Option<String>,
}

#[derive(Debug, Clone, Parser)]
Expand Down