Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic runtime constants #357

Merged
merged 13 commits into from
Oct 5, 2022
279 changes: 212 additions & 67 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ tokio = { version = "1.21", features = ["macros", "rt-multi-thread", "sync", "si
pin-project-lite = "0.2"

# subxt
subxt = { git = "https://github.com/paritytech/subxt" }
subxt = "0.24.0"
scale-value = "0.6.0"

# substrate
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
Expand Down
104 changes: 3 additions & 101 deletions src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,113 +6,16 @@
// polkadot, that only has `const` and `type`s that are used in the runtime, and we can import
// that.

use crate::{prelude::*, static_types};
use codec::{Decode, Encode};
use jsonrpsee::{core::client::ClientT, rpc_params};
use frame_support::{traits::ConstU32, weights::Weight, BoundedVec};
use once_cell::sync::OnceCell;
use pallet_transaction_payment::RuntimeDispatchInfo;
use sp_core::Bytes;
use subxt::rpc::rpc_params;

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

macro_rules! impl_atomic_u32_parameter_types {
($mod:ident, $name:ident) => {
mod $mod {
use std::sync::atomic::{AtomicU32, Ordering};

static VAL: AtomicU32 = AtomicU32::new(0);

pub struct $name;
impl $name {
pub fn get() -> u32 {
VAL.load(Ordering::SeqCst)
}
}

impl<I: From<u32>> frame_support::traits::Get<I> for $name {
fn get() -> I {
I::from(Self::get())
}
}

impl $name {
pub fn set(val: u32) {
VAL.store(val, std::sync::atomic::Ordering::SeqCst);
}
}
}

pub use $mod::$name;
};
}

mod max_weight {
use std::sync::atomic::{AtomicU64, Ordering};

static VAL: AtomicU64 = AtomicU64::new(0);

pub struct MaxWeight;

impl MaxWeight {
pub fn get() -> u64 {
VAL.load(Ordering::SeqCst)
}
}

impl<I: From<u64>> frame_support::traits::Get<I> for MaxWeight {
fn get() -> I {
I::from(Self::get())
}
}

impl MaxWeight {
pub fn set(val: u64) {
VAL.store(val, std::sync::atomic::Ordering::SeqCst);
}
}
}

mod db_weight {
use frame_support::weights::RuntimeDbWeight;
use std::sync::atomic::{AtomicU64, Ordering};

static READ: AtomicU64 = AtomicU64::new(0);
static WRITE: AtomicU64 = AtomicU64::new(0);

pub struct DbWeight;

impl DbWeight {
pub fn get() -> RuntimeDbWeight {
RuntimeDbWeight {
read: READ.load(Ordering::SeqCst),
write: WRITE.load(Ordering::SeqCst),
}
}

pub fn set(weight: RuntimeDbWeight) {
READ.store(weight.read, Ordering::SeqCst);
WRITE.store(weight.write, Ordering::SeqCst)
}
}

impl<I: From<RuntimeDbWeight>> frame_support::traits::Get<I> for DbWeight {
fn get() -> I {
I::from(Self::get())
}
}
}

use crate::prelude::*;
use frame_support::{traits::ConstU32, weights::Weight, BoundedVec};

pub mod static_types {
use super::*;

impl_atomic_u32_parameter_types!(max_length, MaxLength);
impl_atomic_u32_parameter_types!(max_votes_per_voter, MaxVotesPerVoter);
pub use db_weight::DbWeight;
pub use max_weight::MaxWeight;
}

#[cfg(feature = "westend")]
pub mod westend {
use super::*;
Expand Down Expand Up @@ -437,7 +340,6 @@ fn get_weight<T: Encode>(tx: subxt::tx::StaticTxPayload<T>) -> Weight {

let bytes: Bytes = client
.rpc()
.client
.request(
"state_call",
rpc_params!["TransactionPaymentCallApi_query_call_info", call_data],
Expand Down
10 changes: 8 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ pub enum Error {
RpcError(#[from] jsonrpsee::core::Error),
#[error("subxt error: `{0}`")]
Subxt(#[from] subxt::Error),
#[error("Codec error: `{0}`")]
Codec(#[from] codec::Error),
#[error("Crypto error: `{0:?}`")]
Crypto(sp_core::crypto::SecretStringError),
#[error("Incorrect phase")]
Expand All @@ -20,6 +18,14 @@ pub enum Error {
AccountDoesNotExists,
#[error("Submission with better score already exist")]
BetterScoreExist,
#[error("Invalid chain: `{0}`, staking-miner supports only polkadot, kusama and westend")]
InvalidChain(String),
#[error("Other error: `{0}`")]
Other(String),
#[error("Invalid metadata: {0}")]
InvalidMetadata(String),
#[error("Transaction rejected: {0}")]
TransactionRejected(String),
#[error("Subscription closed")]
SubscriptionClosed,
}
118 changes: 78 additions & 40 deletions src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{chain, opt::Solver, prelude::*};
use crate::{chain, opt::Solver, prelude::*, static_types};
use frame_election_provider_support::{PhragMMS, SequentialPhragmen};
use frame_support::BoundedVec;
use frame_support::{weights::Weight, BoundedVec};
use pallet_election_provider_multi_phase::{SolutionOf, SolutionOrSnapshotSize};
use pin_project_lite::pin_project;
use sp_npos_elections::ElectionScore;
Expand Down Expand Up @@ -89,7 +89,7 @@ macro_rules! helpers_for_runtime {

pub async fn [<snapshot_$runtime>](api: &SubxtClient, hash: Option<Hash>) -> Result<crate::chain::$runtime::epm::Snapshot, Error> {
use crate::chain::[<$runtime>]::{epm::RoundSnapshot, runtime};
use crate::chain::static_types;
use crate::static_types;

let RoundSnapshot { voters, targets } = api
.storage().fetch(&runtime::storage().election_provider_multi_phase().snapshot(), hash)
Expand All @@ -114,43 +114,6 @@ macro_rules! helpers_for_runtime {

Ok((voters, targets, desired_targets))
}

pub fn [<update_runtime_constants_$runtime>](api: &SubxtClient) {
use chain::static_types;
use crate::chain::[<$runtime>]::runtime;

// maximum weight of the signed submission is exposed from metadata and MUST be this.
let max_weight = api
.constants()
.at(&runtime::constants().election_provider_multi_phase().miner_max_weight())
.expect("constant `miner_max_weight` must exist")
.ref_time;
let max_length = api
.constants()
.at(&runtime::constants().election_provider_multi_phase().miner_max_length())
.expect("constant `miner_max_length` must exist");
let max_votes_per_voter = api
.constants()
.at(&runtime::constants().election_provider_multi_phase().miner_max_votes_per_voter())
.expect("constant `miner_max_votes_per_voter` must exist");
let db_weight = api
.constants()
.at(&runtime::constants().system().db_weight())
.expect("constant `db_weight` must exist");

let system_db_weight =
frame_support::weights::RuntimeDbWeight { read: db_weight.read, write: db_weight.write };

static_types::DbWeight::set(system_db_weight);
static_types::MaxWeight::set(max_weight);
static_types::MaxLength::set(max_length);
static_types::MaxVotesPerVoter::set(max_votes_per_voter);

log::debug!(target: LOG_TARGET, "Constant `max_weight`: {:?}", static_types::MaxWeight::get());
log::debug!(target: LOG_TARGET, "Constant `max_length`: {:?}", static_types::MaxLength::get());
log::debug!(target: LOG_TARGET, "Constant `max_votes_per_voter`: {:?}", static_types::MaxVotesPerVoter::get());
log::debug!(target: LOG_TARGET, "Constant `db_weight`: {:?}", static_types::DbWeight::get());
}
}
};
}
Expand All @@ -161,3 +124,78 @@ helpers_for_runtime!(polkadot);
helpers_for_runtime!(kusama);
#[cfg(feature = "westend")]
helpers_for_runtime!(westend);

#[derive(Copy, Clone, Debug)]
struct EpmConstant {
epm: &'static str,
constant: &'static str,
}

impl EpmConstant {
const fn new(constant: &'static str) -> Self {
Self { epm: "ElectionProviderMultiPhase", constant }
}

const fn to_parts(&self) -> (&'static str, &'static str) {
(self.epm, self.constant)
}

fn to_string(&self) -> String {
format!("{}::{}", self.epm, self.constant)
}
}

pub(crate) async fn read_metadata_constants(api: &SubxtClient) -> Result<(), Error> {
const SIGNED_MAX_WEIGHT: EpmConstant = EpmConstant::new("SignedMaxWeight");
const MAX_LENGTH: EpmConstant = EpmConstant::new("MinerMaxLength");
const MAX_VOTES_PER_VOTER: EpmConstant = EpmConstant::new("MinerMaxVotesPerVoter");

let max_weight = read_constant::<Weight>(api, SIGNED_MAX_WEIGHT)?;
let max_length: u32 = read_constant(api, MAX_LENGTH)?;
let max_votes_per_voter: u32 = read_constant(api, MAX_VOTES_PER_VOTER)?;

log::trace!(
target: LOG_TARGET,
"updating metadata constant `{}`: {}",
SIGNED_MAX_WEIGHT.to_string(),
max_weight.ref_time()
);
log::trace!(
target: LOG_TARGET,
"updating metadata constant `{}`: {}",
MAX_LENGTH.to_string(),
max_length
);
log::trace!(
target: LOG_TARGET,
"updating metadata constant `{}`: {}",
MAX_VOTES_PER_VOTER.to_string(),
max_votes_per_voter
);

static_types::MaxWeight::set(max_weight);
static_types::MaxLength::set(max_length);
static_types::MaxVotesPerVoter::set(max_votes_per_voter);

Ok(())
}

fn invalid_metadata_error<E: std::error::Error>(item: String, err: E) -> Error {
Error::InvalidMetadata(format!("{} failed: {}", item, err))
}

fn read_constant<'a, T: serde::Deserialize<'a>>(
api: &SubxtClient,
constant: EpmConstant,
) -> Result<T, Error> {
let (epm_name, constant) = constant.to_parts();

let val = api
.constants()
.at(&subxt::dynamic::constant(epm_name, constant))
.map_err(|e| invalid_metadata_error(constant.to_string(), e))?;

scale_value::serde::from_value::<_, T>(val).map_err(|e| {
Error::InvalidMetadata(format!("Decoding `{}` failed {}", std::any::type_name::<T>(), e))
})
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod opt;
pub mod prelude;
pub mod prometheus;
pub mod signer;
pub mod static_types;
Loading