Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ members = [
"common",
"node",
"pallets/*",
"pallets/rate-limiting/runtime-api",
"pallets/rate-limiting/rpc",
"precompiles",
"primitives/*",
"runtime",
Expand Down Expand Up @@ -59,6 +61,9 @@ pallet-subtensor = { path = "pallets/subtensor", default-features = false }
pallet-subtensor-swap = { path = "pallets/swap", default-features = false }
pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default-features = false }
pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false }
pallet-rate-limiting = { path = "pallets/rate-limiting", default-features = false }
pallet-rate-limiting-runtime-api = { path = "pallets/rate-limiting/runtime-api", default-features = false }
pallet-rate-limiting-rpc = { path = "pallets/rate-limiting/rpc", default-features = false }
procedural-fork = { path = "support/procedural-fork", default-features = false }
safe-math = { path = "primitives/safe-math", default-features = false }
share-pool = { path = "primitives/share-pool", default-features = false }
Expand Down
45 changes: 45 additions & 0 deletions pallets/rate-limiting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "pallet-rate-limiting"
version = "0.1.0"
edition.workspace = true

[lints]
workspace = true

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { workspace = true, features = ["derive"] }
frame-benchmarking = { workspace = true, optional = true }
frame-support.workspace = true
frame-system.workspace = true
scale-info = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive"], optional = true }
sp-std.workspace = true
subtensor-runtime-common.workspace = true

[dev-dependencies]
sp-core.workspace = true
sp-io.workspace = true
sp-runtime.workspace = true

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
"serde",
"sp-std/std",
"subtensor-runtime-common/std",
]
runtime-benchmarks = [
"frame-benchmarking",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
]
22 changes: 22 additions & 0 deletions pallets/rate-limiting/rpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "pallet-rate-limiting-rpc"
version = "0.1.0"
description = "RPC interface for the rate limiting pallet"
edition.workspace = true

[dependencies]
jsonrpsee = { workspace = true, features = ["client-core", "server", "macros"] }
sp-api.workspace = true
sp-blockchain.workspace = true
sp-runtime.workspace = true
pallet-rate-limiting-runtime-api.workspace = true
subtensor-runtime-common = { workspace = true, default-features = false }

[features]
default = ["std"]
std = [
"sp-api/std",
"sp-runtime/std",
"pallet-rate-limiting-runtime-api/std",
"subtensor-runtime-common/std",
]
82 changes: 82 additions & 0 deletions pallets/rate-limiting/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! RPC interface for the rate limiting pallet.

use jsonrpsee::{
core::RpcResult,
proc_macros::rpc,
types::{ErrorObjectOwned, error::ErrorObject},
};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_runtime::traits::Block as BlockT;
use std::sync::Arc;

pub use pallet_rate_limiting_runtime_api::{RateLimitRpcResponse, RateLimitingRuntimeApi};

#[rpc(client, server)]
pub trait RateLimitingRpcApi<BlockHash> {
#[method(name = "rateLimiting_getRateLimit")]
fn get_rate_limit(
&self,
pallet: Vec<u8>,
extrinsic: Vec<u8>,
at: Option<BlockHash>,
) -> RpcResult<Option<RateLimitRpcResponse>>;
}

/// Error type of this RPC api.
pub enum Error {
/// The call to runtime failed.
RuntimeError(String),
}

impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
match e {
Error::RuntimeError(e) => ErrorObject::owned(1, e, None::<()>),
}
}
}

impl From<Error> for i32 {
fn from(e: Error) -> i32 {
match e {
Error::RuntimeError(_) => 1,
}
}
}

/// RPC implementation for the rate limiting pallet.
pub struct RateLimiting<C, Block> {
client: Arc<C>,
_marker: std::marker::PhantomData<Block>,
}

impl<C, Block> RateLimiting<C, Block> {
/// Creates a new instance of the rate limiting RPC helper.
pub fn new(client: Arc<C>) -> Self {
Self {
client,
_marker: Default::default(),
}
}
}

impl<C, Block> RateLimitingRpcApiServer<<Block as BlockT>::Hash> for RateLimiting<C, Block>
where
Block: BlockT,
C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static,
C::Api: RateLimitingRuntimeApi<Block>,
{
fn get_rate_limit(
&self,
pallet: Vec<u8>,
extrinsic: Vec<u8>,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Option<RateLimitRpcResponse>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

api.get_rate_limit(at, pallet, extrinsic)
.map_err(|e| Error::RuntimeError(format!("Unable to fetch rate limit: {e:?}")).into())
}
}
26 changes: 26 additions & 0 deletions pallets/rate-limiting/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "pallet-rate-limiting-runtime-api"
version = "0.1.0"
description = "Runtime API for the rate limiting pallet"
edition.workspace = true

[dependencies]
codec = { workspace = true, features = ["derive"] }
scale-info = { workspace = true, features = ["derive"] }
sp-api.workspace = true
sp-std.workspace = true
pallet-rate-limiting.workspace = true
subtensor-runtime-common = { workspace = true, default-features = false }
serde = { workspace = true, features = ["derive"], optional = true }

[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"sp-api/std",
"sp-std/std",
"pallet-rate-limiting/std",
"subtensor-runtime-common/std",
"serde",
]
25 changes: 25 additions & 0 deletions pallets/rate-limiting/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![cfg_attr(not(feature = "std"), no_std)]

use codec::{Decode, Encode};
use pallet_rate_limiting::RateLimitKind;
use scale_info::TypeInfo;
use sp_std::vec::Vec;
use subtensor_runtime_common::BlockNumber;

#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};

#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct RateLimitRpcResponse {
pub global: Option<RateLimitKind<BlockNumber>>,
pub contextual: Vec<(Vec<u8>, RateLimitKind<BlockNumber>)>,
pub default_limit: BlockNumber,
pub resolved: Option<BlockNumber>,
}

sp_api::decl_runtime_apis! {
pub trait RateLimitingRuntimeApi {
fn get_rate_limit(pallet: Vec<u8>, extrinsic: Vec<u8>) -> Option<RateLimitRpcResponse>;
}
}
91 changes: 91 additions & 0 deletions pallets/rate-limiting/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Benchmarking setup for pallet-rate-limiting
#![cfg(feature = "runtime-benchmarks")]
#![allow(clippy::arithmetic_side_effects)]

use codec::Decode;
use frame_benchmarking::v2::*;
use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor};

use super::*;

pub trait BenchmarkHelper<Call> {
fn sample_call() -> Call;
}

impl<Call> BenchmarkHelper<Call> for ()
where
Call: Decode,
{
fn sample_call() -> Call {
Decode::decode(&mut &[][..]).expect("Provide a call via BenchmarkHelper::sample_call")
}
}

fn sample_call<T: Config>() -> Box<<T as Config>::RuntimeCall>
where
T::BenchmarkHelper: BenchmarkHelper<<T as Config>::RuntimeCall>,
{
Box::new(T::BenchmarkHelper::sample_call())
}

#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn set_rate_limit() {
let call = sample_call::<T>();
let limit = RateLimitKind::<BlockNumberFor<T>>::Exact(BlockNumberFor::<T>::from(10u32));
let scope = <T as Config>::LimitScopeResolver::context(call.as_ref());
let identifier =
TransactionIdentifier::from_call::<T, ()>(call.as_ref()).expect("identifier");

#[extrinsic_call]
_(RawOrigin::Root, call, limit.clone());

let stored = Limits::<T, ()>::get(&identifier).expect("limit stored");
match (scope, &stored) {
(Some(ref sc), RateLimit::Scoped(map)) => {
assert_eq!(map.get(sc), Some(&limit));
}
(None, RateLimit::Global(kind)) | (Some(_), RateLimit::Global(kind)) => {
assert_eq!(kind, &limit);
}
(None, RateLimit::Scoped(map)) => {
assert!(map.values().any(|k| k == &limit));
}
}
}

#[benchmark]
fn clear_rate_limit() {
let call = sample_call::<T>();
let limit = RateLimitKind::<BlockNumberFor<T>>::Exact(BlockNumberFor::<T>::from(10u32));
let scope = <T as Config>::LimitScopeResolver::context(call.as_ref());

// Pre-populate limit for benchmark call
let identifier =
TransactionIdentifier::from_call::<T, ()>(call.as_ref()).expect("identifier");
match scope.clone() {
Some(sc) => Limits::<T, ()>::insert(identifier, RateLimit::scoped_single(sc, limit)),
None => Limits::<T, ()>::insert(identifier, RateLimit::global(limit)),
}

#[extrinsic_call]
_(RawOrigin::Root, call);

assert!(Limits::<T, ()>::get(identifier).is_none());
}

#[benchmark]
fn set_default_rate_limit() {
let block_span = BlockNumberFor::<T>::from(10u32);

#[extrinsic_call]
_(RawOrigin::Root, block_span);

assert_eq!(DefaultLimit::<T, ()>::get(), block_span);
}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
}
Loading
Loading