Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
51af46b
feat: add `feasibility_check` on mined solution
niklasad1 Mar 7, 2023
639081e
update substrate
niklasad1 Mar 21, 2023
8c5bd8d
update metadata
niklasad1 Mar 21, 2023
2a88e08
fix miner tests
niklasad1 Mar 21, 2023
438ef0e
Merge remote-tracking branch 'origin/main' into na-add-feasibility-check
niklasad1 Mar 21, 2023
1391dca
update staking miner playground
niklasad1 Mar 21, 2023
3a8483b
remove noise
niklasad1 Mar 22, 2023
6e5588e
rewrite me
niklasad1 Mar 28, 2023
274de27
Merge remote-tracking branch 'origin/main' into na-staking-miner-trim…
niklasad1 Apr 26, 2023
0ae0909
make it more readable
niklasad1 Apr 26, 2023
ce2c5a4
fix trimming test: assert trimmed > 0
niklasad1 May 1, 2023
48e4820
test: trimming assert solution mined
niklasad1 May 2, 2023
fde53cb
hack hack
niklasad1 May 2, 2023
33f75de
Merge remote-tracking branch 'origin/main' into na-staking-miner-trim…
niklasad1 May 4, 2023
300af78
Merge remote-tracking branch 'origin/main' into na-staking-miner-trim…
niklasad1 May 5, 2023
25ff30b
doesnt work yet
niklasad1 May 5, 2023
32d7b0e
make it work wooo
niklasad1 May 8, 2023
dc77514
Merge remote-tracking branch 'origin/main' into na-staking-miner-trim…
niklasad1 May 8, 2023
db645bf
cleanup
niklasad1 May 8, 2023
1367088
cargo fmt
niklasad1 May 8, 2023
1c6759a
rename macro
niklasad1 May 8, 2023
20c0be5
fix warnings
niklasad1 May 8, 2023
41b99a9
ci: staking-miner-playground --features test-trimming
niklasad1 May 8, 2023
cc65939
Update src/epm.rs
niklasad1 May 8, 2023
a666181
cargo fmt-2022-11-06
niklasad1 May 9, 2023
52d7e38
Merge remote-tracking branch 'origin/na-staking-miner-trimming-test' …
niklasad1 May 9, 2023
2a3f8bb
cleanup
niklasad1 May 9, 2023
77ec1cf
Update staking-miner-playground/node/src/chain_spec.rs
niklasad1 May 17, 2023
caf4396
address grumbles
niklasad1 May 17, 2023
b0e80c2
Merge remote-tracking branch 'origin/na-staking-miner-trimming-test' …
niklasad1 May 17, 2023
b3b0ccd
Update src/epm.rs
niklasad1 May 18, 2023
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
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ nightly-test:
- cd -
- rm -rf polkadot
- cd staking-miner-playground
- cargo build --release
- cargo build --release --features test-trimming
- mv ./target/release/staking-miner-playground /usr/bin
- staking-miner-playground --version
- cd -
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ hyper = { version = "0.14.25", features = ["server", "http1", "http2", "tcp"] }
once_cell = "1.17"

[dev-dependencies]
anyhow = "1"
assert_cmd = "2.0"
sp-storage = "11.0.0"
regex = "1"
Expand Down
30 changes: 15 additions & 15 deletions src/commands/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,14 +313,14 @@ where
.timed()
.await
{
(Ok(miner_solution), elapsed) => {
(Ok(mined_solution), elapsed) => {
// check that the solution looks valid:
miner_solution.feasibility_check()?;
mined_solution.feasibility_check()?;

// and then get the values we need from it:
let solution = miner_solution.solution();
let score = miner_solution.score();
let size = miner_solution.size();
let solution = mined_solution.solution();
let score = mined_solution.score();
let size = mined_solution.size();

let elapsed_ms = elapsed.as_millis();
let encoded_len = solution.encoded_size();
Expand All @@ -333,16 +333,16 @@ where
.await?;

log::info!(
target: LOG_TARGET,
"Mined solution with {:?} size: {:?} round: {:?} at: {}, took: {} ms, len: {:?}, weight = {:?}",
score,
size,
round,
at.number(),
elapsed_ms,
encoded_len,
final_weight,
);
target: LOG_TARGET,
"Mined solution with {:?} size: {:?} round: {:?} at: {}, took: {} ms, len: {:?}, weight = {:?}",
score,
size,
round,
at.number(),
elapsed_ms,
encoded_len,
final_weight,
);

prometheus::set_length(encoded_len);
prometheus::set_weight(final_weight);
Expand Down
8 changes: 8 additions & 0 deletions src/epm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ where
let voters = snapshot.voters.clone();
let targets = snapshot.targets.clone();

log::trace!(
target: LOG_TARGET,
"mine solution: desired_target={}, voters={}, targets={}",
desired_targets,
voters.len(),
targets.len()
);

let blocking_task = tokio::task::spawn_blocking(move || match solver {
Solver::SeqPhragmen { iterations } => {
BalanceIterations::set(iterations);
Expand Down
3 changes: 2 additions & 1 deletion staking-miner-playground/node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ fn testnet_genesis(wasm_binary: &[u8], _enable_println: bool) -> GenesisConfig {
let initial_authorities = vec![authority_keys_from_seed("Alice")]
.into_iter()
.chain(
(nominators..nominators + candidates)
// because Alice is already inserted above only candidates-1 needs to generated.
(0..candidates - 1)
.map(|_| rand_str())
.map(|seed| authority_keys_from_seed(seed.as_str())),
)
Expand Down
2 changes: 1 addition & 1 deletion staking-miner-playground/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ std = [
"sp-transaction-pool/std",
"sp-version/std",
]

runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
]
try-runtime = []
test-trimming = []
62 changes: 47 additions & 15 deletions staking-miner-playground/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

pub mod pallet_config_block;

#[macro_export]
macro_rules! prod_or_enforce_trimming {
($prod:expr, $test:expr) => {
if cfg!(feature = "test-trimming") {
$test
} else {
$prod
}
};
}

use election_multi_phase::SolutionAccuracyOf;
use frame_election_provider_support::{onchain, ElectionDataProvider, SequentialPhragmen};
use frame_support::{
Expand Down Expand Up @@ -57,7 +68,7 @@ pub use pallet_timestamp::Call as TimestampCall;
use pallet_transaction_payment::CurrencyAdapter;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};
pub use sp_runtime::{Perbill, Percent, Permill};

/// An index to a block.
pub type BlockNumber = u32;
Expand Down Expand Up @@ -450,15 +461,41 @@ parameter_types! {

// miner configs
pub const ElectionUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64;
pub MinerMaxWeight: Weight = Perbill::from_rational(8u32, 10) *
<Runtime as frame_system::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.max_total
.unwrap();
pub MinerMaxLength: u32 = Perbill::from_rational(8u32, 10) *
*(<<Runtime as frame_system::Config>::BlockLength as Get<limits::BlockLength>>::get())
.max
.get(DispatchClass::Normal);

// This is a hack to get the number of validators, candidates and nominators
// used by node which uses the same env flags as the chain spec builder in the node crate.
Nominators: u32 = option_env!("N").unwrap_or("1000").parse().expect("env variable `N` must be number");
Candidates: u32 = option_env!("C").unwrap_or("500").parse().expect("env variable `C` must be number");
Validators: u32 = option_env!("V").unwrap_or("100").parse().expect("env variable `V` must be number");

BlockLength: u32 = Perbill::from_rational(8u32, 10) * *(<<Runtime as frame_system::Config>::BlockLength as Get<limits::BlockLength>>::get()).max.get(DispatchClass::Normal);

// TODO: `trimming` will only work with the default values on `Nominators, Candidates and Validators`.
//
// The value was retrieved by something like:
//
// ```
// let voters = 1000;
// let targets = 500;
// let active_voters = 1000;
// let desired_targets = 100;
// let weight = MinerConfig::solution_weight(voters, targets, active_voters, desired_targets) * Perbill::from_percent(95)
// ```
WeightTrimming: Weight = Weight::from_parts(9226276000, 3905328);

pub MinerMaxLength: u32 = prod_or_enforce_trimming!(
BlockLength::get(),
Perbill::from_percent(90) * BlockLength::get()
);

pub MinerMaxWeight: Weight = prod_or_enforce_trimming!(
Perbill::from_rational(8u32, 10) * <Runtime as frame_system::Config>::BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap(),
WeightTrimming::get()
);

// The maximum winners that can be elected by the Election pallet which is equivalent to the
// maximum active validators the staking pallet can have.
pub MaxActiveValidators: u32 = Validators::get();
}

mod solution_16 {
Expand Down Expand Up @@ -502,10 +539,6 @@ parameter_types! {
pub const MaxElectingVoters: u32 = 25_000;
pub MaxOnChainElectingVoters: u32 = 5000;
pub MaxOnChainElectableTargets: u16 = 1250;
// The maximum winners that can be elected by the Election pallet which is equivalent to the
// maximum active validators the staking pallet can have.
pub MaxActiveValidators: u32 = 1000;

}

/// The numbers configured here could always be more than the the maximum limits of staking pallet
Expand Down Expand Up @@ -600,7 +633,6 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type MaxElectableTargets = ConstU16<{ u16::MAX }>;
type MaxElectingVoters = MaxElectingVoters;
type MaxWinners = MaxActiveValidators;
// type MaxElectingVoters = IncPerRound<10_000, 1000>;
type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
}

Expand Down
96 changes: 94 additions & 2 deletions tests/common.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
use staking_miner::opt::Chain;
use assert_cmd::cargo::cargo_bin;
use codec::Decode;
use sp_storage::StorageChangeSet;
use staking_miner::{
opt::Chain,
prelude::{
runtime::{self},
sp_core::Bytes,
Hash, SubxtClient,
},
};
use std::{
io::{BufRead, BufReader, Read},
net::SocketAddr,
ops::{Deref, DerefMut},
process::{self, Child, ChildStderr, ChildStdout},
time::{Duration, Instant},
};
use subxt::rpc::rpc_params;
use tokio::time::timeout;
use tracing_subscriber::EnvFilter;

pub use runtime::runtime_types::pallet_election_provider_multi_phase::{
ElectionCompute, ReadySolution,
};

pub const MAX_DURATION_FOR_SUBMIT_SOLUTION: Duration = Duration::from_secs(60 * 15);

pub fn init_logger() {
let _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
Expand Down Expand Up @@ -57,7 +76,7 @@ pub fn run_staking_miner_playground() -> (KillChildOnDrop, String) {
process::Command::new("staking-miner-playground")
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(&["--dev"])
.args(&["--dev", "--offchain-worker=Never"])
.env("RUST_LOG", "runtime=debug")
.spawn()
.unwrap(),
Expand Down Expand Up @@ -140,3 +159,76 @@ pub fn spawn_cli_output_threads(
}
});
}

pub enum Target {
PolkadotNode(Chain),
StakingMinerPlayground,
}

pub async fn test_submit_solution(target: Target) {
let (_drop, ws_url) = match target {
Target::PolkadotNode(chain) => run_polkadot_node(chain),
Target::StakingMinerPlayground => run_staking_miner_playground(),
};

let mut miner = KillChildOnDrop(
process::Command::new(cargo_bin(env!("CARGO_PKG_NAME")))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(&["--uri", &ws_url, "monitor", "--seed-or-path", "//Alice", "seq-phragmen"])
.spawn()
.unwrap(),
);

let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
spawn_cli_output_threads(miner.stdout.take().unwrap(), miner.stderr.take().unwrap(), tx);

tokio::spawn(async move {
let r = rx.recv().await.unwrap();
log::info!("{}", r);
});

let ready_solution = wait_for_mined_solution(&ws_url).await.unwrap();
assert!(ready_solution.compute == ElectionCompute::Signed);
}

/// Wait until a solution is ready on chain
///
/// Timeout's after 15 minutes which is regarded as an error.
pub async fn wait_for_mined_solution(ws_url: &str) -> anyhow::Result<ReadySolution> {
let api = SubxtClient::from_url(&ws_url).await?;
let now = Instant::now();

let key = Bytes(
runtime::storage()
.election_provider_multi_phase()
.queued_solution()
.to_root_bytes(),
);

let mut sub = api
.rpc()
.subscribe("state_subscribeStorage", rpc_params![vec![key]], "state_unsubscribeStorage")
.await
.unwrap();

while now.elapsed() < MAX_DURATION_FOR_SUBMIT_SOLUTION {
let x: StorageChangeSet<Hash> =
match timeout(MAX_DURATION_FOR_SUBMIT_SOLUTION, sub.next()).await {
Err(e) => return Err(e.into()),
Ok(Some(Ok(storage))) => storage,
Ok(None) => return Err(anyhow::anyhow!("Subscription closed")),
Ok(Some(Err(e))) => return Err(e.into()),
};

if let Some(data) = x.changes[0].clone().1 {
let solution: ReadySolution = Decode::decode(&mut data.0.as_slice())?;
return Ok(solution)
}
}

Err(anyhow::anyhow!(
"ReadySolution not found in {}s regarded as error",
MAX_DURATION_FOR_SUBMIT_SOLUTION.as_secs()
))
}
Loading