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
60 changes: 54 additions & 6 deletions src/commands/emergency_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

//! The emergency-solution command.

use crate::{error::Error, opt::Solver, prelude::*, static_types};
use crate::{epm, error::Error, opt::Solver, prelude::*, static_types};
use clap::Parser;
use codec::Encode;
use std::io::Write;

#[derive(Debug, Clone, Parser)]
#[cfg_attr(test, derive(PartialEq))]
Expand All @@ -30,13 +32,13 @@ pub struct EmergencySolutionConfig {
#[clap(subcommand)]
pub solver: Solver,

/// The number of top backed winners to take. All are taken, if not provided.
pub take: Option<usize>,
/// The number of top backed winners to take instead. All are taken, if not provided.
pub force_winner_count: Option<u32>,
}

pub async fn emergency_solution_cmd<T>(
_api: SubxtClient,
_config: EmergencySolutionConfig,
api: SubxtClient,
config: EmergencySolutionConfig,
) -> Result<(), Error>
where
T: MinerConfig<AccountId = AccountId, MaxVotesPerVoter = static_types::MaxVotesPerVoter>
Expand All @@ -45,5 +47,51 @@ where
+ 'static,
T::Solution: Send,
{
todo!("not possible to implement yet");
let round = api
.storage()
.at(config.at)
.await?
.fetch_or_default(&runtime::storage().election_provider_multi_phase().round())
.await?;

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

let ready_solution = miner_solution.feasibility_check()?;
let encoded_size = ready_solution.encoded_size();
let score = ready_solution.score;

let mut supports = ready_solution.supports.into_inner();

// maybe truncate.
if let Some(force_winner_count) = config.force_winner_count {
log::info!(
target: LOG_TARGET,
"truncating {} winners to {}",
supports.len(),
force_winner_count
);
supports.sort_unstable_by_key(|(_, s)| s.total);
supports.truncate(force_winner_count as usize);
}

// write to file and stdout.
let encoded_supports = supports.encode();
let mut supports_file = std::fs::File::create("solution.supports.bin")?;
supports_file.write_all(&encoded_supports)?;

log::info!(target: LOG_TARGET, "ReadySolution: size {:?} / score = {:?}", encoded_size, score);
log::trace!(
target: LOG_TARGET,
"Supports: {:?}",
sp_core::hexdisplay::HexDisplay::from(&encoded_supports)
);

Ok(())
}
18 changes: 15 additions & 3 deletions src/epm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
use codec::{Decode, Encode};
use frame_election_provider_support::{NposSolution, PhragMMS, SequentialPhragmen};
use frame_support::weights::Weight;
use pallet_election_provider_multi_phase::{RawSolution, SolutionOrSnapshotSize};
use pallet_election_provider_multi_phase::{RawSolution, ReadySolution, SolutionOrSnapshotSize};
use scale_info::{PortableRegistry, TypeInfo};
use scale_value::scale::{decode_as_type, TypeId};
use sp_core::Bytes;
Expand Down Expand Up @@ -112,6 +112,16 @@ fn read_constant<'a, T: serde::Deserialize<'a>>(
})
}

/// Helper to construct a set emergency solution transaction.
pub fn set_emergency_result<A: Encode + TypeInfo + 'static>(
supports: frame_election_provider_support::Supports<A>,
) -> Result<DynamicTxPayload<'static>, Error> {
let scale_result = to_scale_value(supports)
.map_err(|e| Error::DynamicTransaction(format!("Failed to decode `Supports`: {:?}", e)))?;

Ok(subxt::dynamic::tx(EPM_PALLET_NAME, "set_emergency_result", vec![scale_result]))
}

/// Helper to construct a signed solution transaction.
pub fn signed_solution<S: NposSolution + Encode + TypeInfo + 'static>(
solution: RawSolution<S>,
Expand Down Expand Up @@ -272,7 +282,9 @@ where
}

/// Check that this solution is feasible
pub fn feasibility_check(&self) -> Result<(), Error> {
///
/// Returns a [`pallet_election_provider_multi_phase::ReadySolution`] if the check passes.
pub fn feasibility_check(&self) -> Result<ReadySolution<AccountId, T::MaxWinners>, Error> {
match Miner::<T>::feasibility_check(
RawSolution { solution: self.solution.clone(), score: self.score, round: self.round },
pallet_election_provider_multi_phase::ElectionCompute::Signed,
Expand All @@ -281,7 +293,7 @@ where
self.round,
self.minimum_untrusted_score,
) {
Ok(_) => Ok(()),
Ok(ready_solution) => Ok(ready_solution),
Err(e) => {
log::error!(target: LOG_TARGET, "Solution feasibility error {:?}", e);
Err(Error::Feasibility(format!("{:?}", e)))
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ mod tests {
prometheus_port: None,
log: "info".to_string(),
command: Command::EmergencySolution(commands::EmergencySolutionConfig {
take: Some(99),
force_winner_count: Some(99),
at: None,
solver: opt::Solver::PhragMMS { iterations: 1337 },
}),
Expand Down