Skip to content

Commit

Permalink
Merge pull request #237 from paritytech/na-hacky-use-runtime-fetch-we…
Browse files Browse the repository at this point in the history
…ight

hacky: fetch weight from remote node
  • Loading branch information
niklasad1 authored Sep 5, 2022
2 parents b2bc474 + d4f9892 commit 62e2124
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 37 deletions.
17 changes: 17 additions & 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 @@ -27,6 +27,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-election-provider-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-election-provider-multi-phase = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-npos-elections = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" }
Expand Down
192 changes: 162 additions & 30 deletions src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
// polkadot, that only has `const` and `type`s that are used in the runtime, and we can import
// that.

use codec::{Decode, Encode};
use jsonrpsee::{core::client::ClientT, rpc_params};
use once_cell::sync::OnceCell;
use pallet_transaction_payment::RuntimeDispatchInfo;
use sp_core::Bytes;

pub static SHARED_CLIENT: OnceCell<SubxtClient> = OnceCell::new();

macro_rules! impl_atomic_u32_parameter_types {
($mod:ident, $name:ident) => {
mod $mod {
Expand Down Expand Up @@ -130,16 +138,35 @@ pub mod westend {
type Solution = NposSolution16;

// SYNC
fn solution_weight(v: u32, _t: u32, a: u32, d: u32) -> Weight {
// feasibility weight.
(31_722_000 as Weight)
// Standard Error: 8_000
.saturating_add((1_255_000 as Weight).saturating_mul(v as Weight))
// Standard Error: 28_000
.saturating_add((8_972_000 as Weight).saturating_mul(a as Weight))
// Standard Error: 42_000
.saturating_add((966_000 as Weight).saturating_mul(d as Weight))
.saturating_add(static_types::DbWeight::get().reads(4 as Weight))
fn solution_weight(
voters: u32,
targets: u32,
active_voters: u32,
desired_targets: u32,
) -> Weight {
use _feps::NposSolution;
use pallet_election_provider_multi_phase::{RawSolution, SolutionOrSnapshotSize};

// Mock a RawSolution to get the correct weight without having to do the heavy work.
let raw = RawSolution {
solution: NposSolution16 {
votes1: mock_votes(
active_voters,
desired_targets.try_into().expect("Desired targets < u16::MAX"),
),
..Default::default()
},
..Default::default()
};

assert_eq!(raw.solution.voter_count(), active_voters as usize);
assert_eq!(raw.solution.unique_targets().len(), desired_targets as usize);

let tx = runtime::tx()
.election_provider_multi_phase()
.submit_unsigned(raw, SolutionOrSnapshotSize { voters, targets });

get_weight(tx)
}
}

Expand All @@ -166,6 +193,11 @@ pub mod westend {

#[subxt(substitute_type = "pallet_election_provider_multi_phase::Phase")]
use ::pallet_election_provider_multi_phase::Phase;

#[subxt(
substitute_type = "pallet_election_provider_multi_phase::SolutionOrSnapshotSize"
)]
use ::pallet_election_provider_multi_phase::SolutionOrSnapshotSize;
}

pub use runtime::runtime_types;
Expand Down Expand Up @@ -206,16 +238,35 @@ pub mod polkadot {
type Solution = NposSolution16;

// SYNC
fn solution_weight(v: u32, _t: u32, a: u32, d: u32) -> Weight {
// feasibility weight.
(31_722_000 as Weight)
// Standard Error: 8_000
.saturating_add((1_255_000 as Weight).saturating_mul(v as Weight))
// Standard Error: 28_000
.saturating_add((8_972_000 as Weight).saturating_mul(a as Weight))
// Standard Error: 42_000
.saturating_add((966_000 as Weight).saturating_mul(d as Weight))
.saturating_add(static_types::DbWeight::get().reads(4 as Weight))
fn solution_weight(
voters: u32,
targets: u32,
active_voters: u32,
desired_targets: u32,
) -> Weight {
use _feps::NposSolution;
use pallet_election_provider_multi_phase::{RawSolution, SolutionOrSnapshotSize};

// Mock a RawSolution to get the correct weight without having to do the heavy work.
let raw = RawSolution {
solution: NposSolution16 {
votes1: mock_votes(
active_voters,
desired_targets.try_into().expect("Desired targets < u16::MAX"),
),
..Default::default()
},
..Default::default()
};

assert_eq!(raw.solution.voter_count(), active_voters as usize);
assert_eq!(raw.solution.unique_targets().len(), desired_targets as usize);

let tx = runtime::tx()
.election_provider_multi_phase()
.submit_unsigned(raw, SolutionOrSnapshotSize { voters, targets });

get_weight(tx)
}
}

Expand All @@ -242,6 +293,11 @@ pub mod polkadot {

#[subxt(substitute_type = "pallet_election_provider_multi_phase::Phase")]
use ::pallet_election_provider_multi_phase::Phase;

#[subxt(
substitute_type = "pallet_election_provider_multi_phase::SolutionOrSnapshotSize"
)]
use ::pallet_election_provider_multi_phase::SolutionOrSnapshotSize;
}

pub use runtime::runtime_types;
Expand Down Expand Up @@ -282,16 +338,35 @@ pub mod kusama {
type Solution = NposSolution24;

// SYNC
fn solution_weight(v: u32, _t: u32, a: u32, d: u32) -> Weight {
// feasibility weight.
(31_722_000 as Weight)
// Standard Error: 8_000
.saturating_add((1_255_000 as Weight).saturating_mul(v as Weight))
// Standard Error: 28_000
.saturating_add((8_972_000 as Weight).saturating_mul(a as Weight))
// Standard Error: 42_000
.saturating_add((966_000 as Weight).saturating_mul(d as Weight))
.saturating_add(static_types::DbWeight::get().reads(4 as Weight))
fn solution_weight(
voters: u32,
targets: u32,
active_voters: u32,
desired_targets: u32,
) -> Weight {
use _feps::NposSolution;
use pallet_election_provider_multi_phase::{RawSolution, SolutionOrSnapshotSize};

// Mock a RawSolution to get the correct weight without having to do the heavy work.
let raw = RawSolution {
solution: NposSolution24 {
votes1: mock_votes(
active_voters,
desired_targets.try_into().expect("Desired targets < u16::MAX"),
),
..Default::default()
},
..Default::default()
};

assert_eq!(raw.solution.voter_count(), active_voters as usize);
assert_eq!(raw.solution.unique_targets().len(), desired_targets as usize);

let tx = runtime::tx()
.election_provider_multi_phase()
.submit_unsigned(raw, SolutionOrSnapshotSize { voters, targets });

get_weight(tx)
}
}

Expand All @@ -318,6 +393,11 @@ pub mod kusama {

#[subxt(substitute_type = "pallet_election_provider_multi_phase::Phase")]
use ::pallet_election_provider_multi_phase::Phase;

#[subxt(
substitute_type = "pallet_election_provider_multi_phase::SolutionOrSnapshotSize"
)]
use ::pallet_election_provider_multi_phase::SolutionOrSnapshotSize;
}

pub use runtime::runtime_types;
Expand All @@ -333,3 +413,55 @@ pub mod kusama {
};
}
}

/// Helper to fetch the weight from a remote node
///
/// Panics: if the RPC call fails or if decoding the response as a `Weight` fails.
fn get_weight<T: Encode>(tx: subxt::tx::StaticTxPayload<T>) -> Weight {
futures::executor::block_on(async {
let client = SHARED_CLIENT.get().expect("shared client is configured as start; qed");

let call_data = {
let mut buffer = Vec::new();

let encoded_call = client.tx().call_data(&tx).unwrap();
let encoded_len = encoded_call.len() as u32;

buffer.extend(encoded_call);
encoded_len.encode_to(&mut buffer);

Bytes(buffer)
};

let bytes: Bytes = client
.rpc()
.client
.request(
"state_call",
rpc_params!["TransactionPaymentCallApi_query_call_info", call_data],
)
.await
.unwrap();

let info: RuntimeDispatchInfo<Balance> = Decode::decode(&mut bytes.0.as_ref()).unwrap();

log::debug!(
target: LOG_TARGET,
"Received weight of `Solution Extrinsic` from remote node: {:?}",
info.weight
);

info.weight
})
}

fn mock_votes(voters: u32, desired_targets: u16) -> Vec<(u32, u16)> {
assert!(voters >= desired_targets as u32);
(0..voters).zip((0..desired_targets).cycle()).collect()
}

#[cfg(test)]
fn mock_votes_works() {
assert_eq!(mock_votes(3, 2), vec![(0, 0), (1, 1), (2, 0)]);
assert_eq!(mock_votes(3, 3), vec![(0, 0), (1, 1), (2, 2)]);
}
18 changes: 16 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,29 @@ async fn main() -> Result<(), Error> {
let Opt { uri, command, prometheus_port } = Opt::parse();
log::debug!(target: LOG_TARGET, "attempting to connect to {:?}", uri);

let rpc = WsClientBuilder::default().max_request_body_size(u32::MAX).build(uri).await?;
let api = SubxtClient::from_rpc_client(rpc).await?;
let rpc = loop {
match WsClientBuilder::default().max_request_body_size(u32::MAX).build(&uri).await {
Ok(rpc) => break rpc,
Err(e) => {
log::warn!(
target: LOG_TARGET,
"failed to connect to client due to {:?}, retrying soon..",
e,
);
},
};
tokio::time::sleep(std::time::Duration::from_millis(2_500)).await;
};

let api = SubxtClient::from_rpc_client(rpc).await?;
let runtime_version = api.rpc().runtime_version(None).await?;
let chain = Chain::try_from(runtime_version)?;
let _prometheus_handle = prometheus::run(prometheus_port.unwrap_or(DEFAULT_PROMETHEUS_PORT))
.map_err(|e| log::warn!("Failed to start prometheus endpoint: {}", e));
log::info!(target: LOG_TARGET, "Connected to chain: {}", chain);

chain::SHARED_CLIENT.set(api.clone()).expect("shared client only set once; qed");

let outcome = any_runtime!(chain, {
tls_update_runtime_constants(&api);

Expand Down
12 changes: 7 additions & 5 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ macro_rules! monitor_cmd_for {
return;
}

let addr = runtime::storage().election_provider_multi_phase().round();
let round = match api.storage().fetch(&addr, Some(hash)).await {
let round = match api.storage().fetch(&runtime::storage().election_provider_multi_phase().round(), Some(hash)).await {
Ok(Some(round)) => round,
Ok(None) => unreachable!("Round must always exist"),
// Default round is 1
// https://github.com/paritytech/substrate/blob/49b06901eb65f2c61ff0934d66987fd955d5b8f5/frame/election-provider-multi-phase/src/lib.rs#L1188
Ok(None) => 1,
Err(e) => {
log::error!(target: LOG_TARGET, "Mining solution failed: {:?}", e);

kill_main_task_if_critical_err(&tx, e.into());
return;
},
Expand Down Expand Up @@ -219,7 +219,9 @@ macro_rules! monitor_cmd_for {
match res {
Ok(Some(Phase::Signed)) => Ok(()),
Ok(Some(_)) => Err(Error::IncorrectPhase),
Ok(None) => unreachable!("Phase should always exist"),
// Default phase is None
// https://github.com/paritytech/substrate/blob/49b06901eb65f2c61ff0934d66987fd955d5b8f5/frame/election-provider-multi-phase/src/lib.rs#L1193
Ok(None) => Err(Error::IncorrectPhase),
Err(e) => Err(e.into()),
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub type Header =
subxt::ext::sp_runtime::generic::Header<u32, subxt::ext::sp_runtime::traits::BlakeTwo256>;
/// The header type. We re-export it here, but we can easily get it from block as well.
pub type Hash = sp_core::H256;
/// Balance type
pub type Balance = u128;

pub use subxt::ext::{
sp_core,
Expand Down

0 comments on commit 62e2124

Please sign in to comment.