diff --git a/node/src/client.rs b/node/src/client.rs index c7196b5a9..bd4fcc676 100644 --- a/node/src/client.rs +++ b/node/src/client.rs @@ -51,6 +51,7 @@ pub trait RuntimeApiCollection< + subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi + subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi + subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi + + subtensor_custom_rpc_runtime_api::RateLimitInfoRuntimeApi { } @@ -71,6 +72,7 @@ where + subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi + subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi + subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi - + subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi, + + subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi + + subtensor_custom_rpc_runtime_api::RateLimitInfoRuntimeApi, { } diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index dc3f0017e..1d3435ab8 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use sp_api::ProvideRuntimeApi; pub use subtensor_custom_rpc_runtime_api::{ - DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, SubnetInfoRuntimeApi, + DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, RateLimitInfoRuntimeApi, SubnetInfoRuntimeApi, SubnetRegistrationRuntimeApi, }; @@ -59,6 +59,22 @@ pub trait SubtensorCustomApi { fn get_all_dynamic_info(&self, at: Option) -> RpcResult>; #[method(name = "subnetInfo_getSubnetState")] fn get_subnet_state(&self, netuid: u16, at: Option) -> RpcResult>; + #[method(name = "rateLimitInfo_getRateLimits")] + fn get_rate_limits(&self, at: Option) -> RpcResult>; + #[method(name = "rateLimitInfo_getLimitedTxInfoForHotkey")] + fn get_limited_tx_info_for_hotkey( + &self, + hotkey: Vec, + netuid: u16, + at: Option, + ) -> RpcResult>; + #[method(name = "rateLimitInfo_getStakesThisInterval")] + fn get_stakes_this_interval( + &self, + coldkey: Vec, + hotkey: Vec, + at: Option, + ) -> RpcResult; } pub struct SubtensorCustom { @@ -99,6 +115,16 @@ impl From for i32 { } } +macro_rules! call_api { + ($self:ident, $at:ident, $err_msg:expr, $name:ident, $($param:ident),* ) => {{ + let api = $self.client.runtime_api(); + let at = $at.unwrap_or_else(|| $self.client.info().best_hash); + + api.$name(at $(,$param)*) + .map_err(|e| Error::RuntimeError(format!("{}: {:?}", $err_msg, e)).into()) + }} +} + impl SubtensorCustomApiServer<::Hash> for SubtensorCustom where Block: BlockT, @@ -107,14 +133,10 @@ where C::Api: NeuronInfoRuntimeApi, C::Api: SubnetInfoRuntimeApi, C::Api: SubnetRegistrationRuntimeApi, + C::Api: RateLimitInfoRuntimeApi, { fn get_delegates(&self, at: Option<::Hash>) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_delegates(at).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + call_api!(self, at, "Unable to get delegates info", get_delegates,) } fn get_delegate( @@ -122,12 +144,13 @@ where delegate_account_vec: Vec, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_delegate(at, delegate_account_vec).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + call_api!( + self, + at, + "Unable to get delegate info", + get_delegate, + delegate_account_vec + ) } fn get_delegated( @@ -135,12 +158,13 @@ where delegatee_account_vec: Vec, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_delegated(at, delegatee_account_vec).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + call_api!( + self, + at, + "Unable to get delegates info", + get_delegated, + delegatee_account_vec + ) } fn get_neurons_lite( @@ -148,12 +172,13 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_neurons_lite(at, netuid).map_err(|e| { - Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into() - }) + call_api!( + self, + at, + "Unable to get neurons lite info", + get_neurons_lite, + netuid + ) } fn get_neuron_lite( @@ -162,20 +187,18 @@ where uid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_neuron_lite(at, netuid, uid).map_err(|e| { - Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into() - }) + call_api!( + self, + at, + "Unable to get neuron lite info", + get_neuron_lite, + netuid, + uid + ) } fn get_neurons(&self, netuid: u16, at: Option<::Hash>) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_neurons(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get neurons info: {:?}", e)).into()) + call_api!(self, at, "Unable to get neurons info", get_neurons, netuid) } fn get_neuron( @@ -184,11 +207,14 @@ where uid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_neuron(at, netuid, uid) - .map_err(|e| Error::RuntimeError(format!("Unable to get neuron info: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get neuron info", + get_neuron, + netuid, + uid + ) } fn get_subnet_state( @@ -196,10 +222,13 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnet_state(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet state: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get subnet state", + get_subnet_state, + netuid + ) } fn get_subnet_info( @@ -207,11 +236,13 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_subnet_info(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get subnet info", + get_subnet_info, + netuid + ) } fn get_subnet_hyperparams( @@ -219,26 +250,21 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_subnet_hyperparams(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get subnet hyperparams", + get_subnet_hyperparams, + netuid + ) } fn get_subnets_info(&self, at: Option<::Hash>) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_subnets_info(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + call_api!(self, at, "Unable to get subnets info", get_subnets_info,) } fn get_all_dynamic_info(&self, at: Option<::Hash>) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_all_dynamic_info(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + call_api!(self, at, "Unable to get subnets info", get_all_dynamic_info,) } fn get_dynamic_info( @@ -246,10 +272,13 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_dynamic_info(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get subnets info", + get_dynamic_info, + netuid + ) } fn get_subnet_info_v2( @@ -257,27 +286,61 @@ where netuid: u16, at: Option<::Hash>, ) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_subnet_info_v2(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + call_api!( + self, + at, + "Unable to get subnet info", + get_subnet_info_v2, + netuid + ) } fn get_subnets_info_v2(&self, at: Option<::Hash>) -> RpcResult> { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); - - api.get_subnets_info_v2(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + call_api!(self, at, "Unable to get subnets info", get_subnets_info_v2,) } fn get_network_lock_cost(&self, at: Option<::Hash>) -> RpcResult { - let api = self.client.runtime_api(); - let at = at.unwrap_or_else(|| self.client.info().best_hash); + call_api!( + self, + at, + "Unable to get subnet lock cost", + get_network_registration_cost, + ) + } + + fn get_rate_limits(&self, at: Option<::Hash>) -> RpcResult> { + call_api!(self, at, "Unable to get rate limits", get_rate_limits,) + } - api.get_network_registration_cost(at).map_err(|e| { - Error::RuntimeError(format!("Unable to get subnet lock cost: {:?}", e)).into() - }) + fn get_limited_tx_info_for_hotkey( + &self, + hotkey: Vec, + netuid: u16, + at: Option<::Hash>, + ) -> RpcResult> { + call_api!( + self, + at, + "Unable to get rate limits info for hotkey", + get_limited_tx_info_for_hotkey, + hotkey, + netuid + ) + } + + fn get_stakes_this_interval( + &self, + coldkey: Vec, + hotkey: Vec, + at: Option<::Hash>, + ) -> RpcResult { + call_api!( + self, + at, + "Unable to get number of stakes for the interval", + get_stakes_this_interval, + coldkey, + hotkey + ) } } diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index f9f3316f3..8cefcfc9c 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -30,11 +30,21 @@ sp_api::decl_runtime_apis! { } pub trait StakeInfoRuntimeApi { - fn get_stake_info_for_coldkey( coldkey_account_vec: Vec ) -> Vec; - fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec> ) -> Vec; + fn get_stake_info_for_coldkey(coldkey_account_vec: Vec) -> Vec; + fn get_stake_info_for_coldkeys(coldkey_account_vecs: Vec>) -> Vec; } pub trait SubnetRegistrationRuntimeApi { fn get_network_registration_cost() -> u64; } + + /// API for getting transaction rate limits associated with coldkeys and hotkeys. + pub trait RateLimitInfoRuntimeApi { + /// Get transactions rate limits. + fn get_rate_limits() -> Vec; + /// Get transaction rate limits associated with the `hotkey`. + fn get_limited_tx_info_for_hotkey(hotkey: Vec, netuid: u16) -> Vec; + /// Get number of stakes associated with coldkey/hotkey pair, made during `StakeInterval`. + fn get_stakes_this_interval(coldkey: Vec, hotkey: Vec) -> u64; + } } diff --git a/pallets/subtensor/src/rpc_info/mod.rs b/pallets/subtensor/src/rpc_info/mod.rs index 4c224050e..b44dc1553 100644 --- a/pallets/subtensor/src/rpc_info/mod.rs +++ b/pallets/subtensor/src/rpc_info/mod.rs @@ -2,6 +2,7 @@ use super::*; pub mod delegate_info; pub mod dynamic_info; pub mod neuron_info; +pub mod rate_limit_info; pub mod show_subnet; pub mod stake_info; pub mod subnet_info; diff --git a/pallets/subtensor/src/rpc_info/rate_limit_info.rs b/pallets/subtensor/src/rpc_info/rate_limit_info.rs new file mode 100644 index 000000000..60b40ad08 --- /dev/null +++ b/pallets/subtensor/src/rpc_info/rate_limit_info.rs @@ -0,0 +1,56 @@ +//! API for getting rate-limited transactions info. +use codec::Compact; +use frame_support::pallet_prelude::{Decode, Encode}; + +use crate::{ + freeze_struct, utils::rate_limiting::TransactionType, Config, Pallet, TargetStakesPerInterval, + TxRateLimit, +}; + +/// Transaction rate limits. +#[freeze_struct("e3734bd0690f2da8")] +#[derive(Decode, Encode, Clone, Debug)] +pub struct RateLimits { + transaction: Compact, + set_children: Compact, + set_childkey_take: Compact, + stakes: Compact, +} + +/// Contains last blocks, when rate-limited transactions was evaluated. +#[freeze_struct("9154106cd720ce08")] +#[derive(Decode, Encode, Clone, Debug)] +pub struct HotkeyLimitedTxInfo { + hotkey: AccountId, + last_block_set_children: Compact, + last_block_set_childkey_take: Compact, +} + +impl Pallet { + /// Get transactions rate limits. + pub fn get_rate_limits() -> RateLimits { + RateLimits { + transaction: TxRateLimit::::get().into(), + set_children: Self::get_rate_limit(&TransactionType::SetChildren).into(), + set_childkey_take: Self::get_rate_limit(&TransactionType::SetChildkeyTake).into(), + stakes: TargetStakesPerInterval::::get().into(), + } + } + + /// Get transaction rate limits associated with the `hotkey`. + pub fn get_limited_tx_info_for_hotkey( + hotkey: &T::AccountId, + netuid: u16, + ) -> HotkeyLimitedTxInfo { + let last_block_set_children = + Self::get_last_transaction_block(hotkey, netuid, &TransactionType::SetChildren).into(); + let last_block_set_childkey_take = + Self::get_last_transaction_block(hotkey, netuid, &TransactionType::SetChildkeyTake) + .into(); + HotkeyLimitedTxInfo { + hotkey: hotkey.clone(), + last_block_set_children, + last_block_set_childkey_take, + } + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 547467f1a..41fdc61f8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -34,6 +34,7 @@ use scale_info::TypeInfo; use smallvec::smallvec; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::hexdisplay::AsBytesRef; use sp_core::{ crypto::{ByteArray, KeyTypeId}, OpaqueMetadata, H160, H256, U256, @@ -2014,6 +2015,24 @@ impl_runtime_apis! { SubtensorModule::get_network_lock_cost() } } + + impl subtensor_custom_rpc_runtime_api::RateLimitInfoRuntimeApi for Runtime { + fn get_rate_limits() -> Vec { + SubtensorModule::get_rate_limits().encode() + } + + fn get_limited_tx_info_for_hotkey(hotkey: Vec, netuid: u16) -> Vec { + let hotkey = AccountId::decode(&mut hotkey.as_bytes_ref()).expect("Could not decode account ID"); + SubtensorModule::get_limited_tx_info_for_hotkey(&hotkey, netuid).encode() + } + + fn get_stakes_this_interval(coldkey: Vec, hotkey: Vec) -> u64 { + let coldkey = AccountId::decode(&mut coldkey.as_bytes_ref()).expect("Could not decode account ID"); + let hotkey = AccountId::decode(&mut hotkey.as_bytes_ref()).expect("Could not decode account ID"); + + SubtensorModule::get_stakes_this_interval_for_coldkey_hotkey(&coldkey, &hotkey) + } + } } // #[cfg(test)]