Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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 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 = []
4 changes: 4 additions & 0 deletions staking-miner-playground/runtime/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use substrate_wasm_builder::WasmBuilder;

pub const NOMINATORS: Option<&str> = option_env!("N");
pub const CANDIDATES: Option<&str> = option_env!("C");
pub const VALIDATORS: Option<&str> = option_env!("V");

fn main() {
WasmBuilder::new()
.with_current_project()
Expand Down
36 changes: 26 additions & 10 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_test {
($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 @@ -450,15 +461,21 @@ 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);

// TODO(niklasad1): this is a hack to get the number of validators, candidates and nominators
// used by node which uses the same env flags.
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");

// Set the max length to `nominators / 2` to force trimming of length to occur.
pub MinerMaxLength: u32 = prod_or_test!(
Perbill::from_rational(8u32, 10) * *(<<Runtime as frame_system::Config>::BlockLength as Get<limits::BlockLength>>::get()).max.get(DispatchClass::Normal),
Nominators::get() / 2
);

// TODO trimming weight seems occur with this anyway.
pub MinerMaxWeight: Weight = Perbill::from_rational(8u32, 10) * <Runtime as frame_system::Config>::BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap();
}

mod solution_16 {
Expand Down Expand Up @@ -600,7 +617,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
87 changes: 85 additions & 2 deletions tests/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
use staking_miner::prelude::Chain;
use assert_cmd::cargo::cargo_bin;
use codec::Decode;
use sp_storage::StorageChangeSet;
use staking_miner::{
opt::Chain,
prelude::{runtime, sp_core::Bytes, Hash, SubxtClient},
};
use std::{
io::{BufRead, BufReader, Read},
ops::{Deref, DerefMut},
process::{self, Child, ChildStderr, ChildStdout},
time::{Duration, Instant},
};
use subxt::rpc::rpc_params;
use tokio::time::timeout;
use tracing_subscriber::EnvFilter;

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 @@ -46,7 +57,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 @@ -129,3 +140,75 @@ pub fn spawn_cli_output_threads(
}
});
}

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

pub async fn test_submit_solution(target: Target) {
use runtime::runtime_types::pallet_election_provider_multi_phase::{
ElectionCompute, ReadySolution,
};

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 api = SubxtClient::from_url(&ws_url).await.unwrap();
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();

let mut success = false;

while now.elapsed() < MAX_DURATION_FOR_SUBMIT_SOLUTION {
let x: StorageChangeSet<Hash> =
match timeout(MAX_DURATION_FOR_SUBMIT_SOLUTION, sub.next()).await {
Err(e) => panic!("Timeout exceeded: {:?}", e),
Ok(Some(Ok(storage))) => storage,
Ok(None) => panic!("Subscription closed"),
Ok(Some(Err(e))) => panic!("Failed to decode StorageChangeSet {:?}", e),
};

if let Some(data) = x.changes[0].clone().1 {
let solution: ReadySolution = Decode::decode(&mut data.0.as_slice())
.expect("Failed to decode storage as QueuedSolution");
eprintln!("solution: {:?}", solution);
assert!(solution.compute == ElectionCompute::Signed);
success = true;
break
}
}

assert!(success);
}
103 changes: 54 additions & 49 deletions tests/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,82 +4,87 @@
pub mod common;

use assert_cmd::cargo::cargo_bin;
use codec::Decode;
use common::{init_logger, run_polkadot_node, KillChildOnDrop};
use sp_storage::StorageChangeSet;
use staking_miner::{
opt::Chain,
prelude::{runtime, sp_core::Bytes, Hash, SubxtClient},
use common::{
init_logger, run_staking_miner_playground, spawn_cli_output_threads, test_submit_solution,
KillChildOnDrop, Target,
};
use regex::Regex;
use staking_miner::opt::Chain;
use std::{
process,
time::{Duration, Instant},
};
use subxt::rpc::rpc_params;
use tokio::time::timeout;

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

#[tokio::test]
async fn submit_monitor_works_basic() {
init_logger();
test_submit_solution(Chain::Polkadot).await;
test_submit_solution(Chain::Kusama).await;
test_submit_solution(Chain::Westend).await;
test_submit_solution(Target::PolkadotNode(Chain::Polkadot)).await;
test_submit_solution(Target::PolkadotNode(Chain::Kusama)).await;
test_submit_solution(Target::PolkadotNode(Chain::Westend)).await;
// It's easier to debug this by just connection a staking miner to
// staking-miner-playground by `./target/debug/staking-miner --uri ws://localhost:9944 monitor --seed-or-path //Alice --listen finalized seq-phragmen`
test_submit_solution(Target::StakingMinerPlayground).await;
}

async fn test_submit_solution(chain: Chain) {
use runtime::runtime_types::pallet_election_provider_multi_phase::{
ElectionCompute, ReadySolution,
};

let (_drop, ws_url) = run_polkadot_node(chain);

let _miner = KillChildOnDrop(
#[tokio::test]
async fn default_trimming_works() {
init_logger();
let (_drop, ws_url) = run_staking_miner_playground();
let miner = KillChildOnDrop(
process::Command::new(cargo_bin(env!("CARGO_PKG_NAME")))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.env("RUST_LOG", "runtime=debug,staking-miner=debug")
.args(&["--uri", &ws_url, "monitor", "--seed-or-path", "//Alice", "seq-phragmen"])
.spawn()
.unwrap(),
);

let api = SubxtClient::from_url(&ws_url).await.unwrap();
assert!(has_trimming_output(miner).await);
}

// Helper that parses the CLI output to find logging outputs based on the following:
//
// i) DEBUG runtime::election-provider: 🗳 from 934 assignments, truncating to 1501 for weight, removing 0
// ii) DEBUG runtime::election-provider: 🗳 from 931 assignments, truncating to 755 for length, removing 176
//
// Thus, the only way to ensure that trimming actually works.
async fn has_trimming_output(mut miner: KillChildOnDrop) -> bool {
let trimming_re = Regex::new(
r#"from (\d+) assignments, truncating to (\d+) for (?P<target>weight|length), removing (\d+)"#,
)
.unwrap();

let mut got_truncate_len = false;
let mut got_truncate_weight = false;

let now = Instant::now();
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();

let key = Bytes(
runtime::storage()
.election_provider_multi_phase()
.queued_solution()
.to_root_bytes(),
);
spawn_cli_output_threads(miner.stdout.take().unwrap(), miner.stderr.take().unwrap(), tx);

let mut sub = api
.rpc()
.subscribe("state_subscribeStorage", rpc_params![vec![key]], "state_unsubscribeStorage")
.await
.unwrap();
while !got_truncate_weight || !got_truncate_len {
let line = rx.recv().await.unwrap();
println!("{}", line);

let mut success = false;
if let Some(caps) = trimming_re.captures(&line) {
if caps.name("target").unwrap().as_str() == "weight" {
got_truncate_weight = true;
}

while now.elapsed() < MAX_DURATION_FOR_SUBMIT_SOLUTION {
let x: StorageChangeSet<Hash> =
match timeout(MAX_DURATION_FOR_SUBMIT_SOLUTION, sub.next()).await {
Err(e) => panic!("Timeout exceeded: {:?}", e),
Ok(Some(Ok(storage))) => storage,
Ok(None) => panic!("Subscription closed"),
Ok(Some(Err(e))) => panic!("Failed to decode StorageChangeSet {:?}", e),
};
if caps.name("target").unwrap().as_str() == "length" {
got_truncate_len = true;
}
}

if got_truncate_weight && got_truncate_len {
return true
}

if let Some(data) = x.changes[0].clone().1 {
let solution: ReadySolution = Decode::decode(&mut data.0.as_slice())
.expect("Failed to decode storage as QueuedSolution");
eprintln!("solution: {:?}", solution);
assert!(solution.compute == ElectionCompute::Signed);
success = true;
if now.elapsed() > Duration::from_secs(5 * 60) {
break
}
}

assert!(success);
false
}
Loading