Skip to content
Merged
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
4 changes: 2 additions & 2 deletions chain-extensions/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 10;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -440,7 +440,7 @@ impl pallet_subtensor::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
4 changes: 2 additions & 2 deletions eco-tests/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 10;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -327,7 +327,7 @@ impl pallet_subtensor::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
type AlphaAssets = AlphaAssets;
}
Expand Down
11 changes: 11 additions & 0 deletions pallets/admin-utils/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,5 +689,16 @@ mod benchmarks {
); /* sudo_set_min_non_immune_uids() */
}

#[benchmark]
fn sudo_set_max_epochs_per_block() {
#[extrinsic_call]
_(RawOrigin::Root, 8u8);

assert_eq!(
pallet_subtensor::Pallet::<T>::get_max_epochs_per_block(),
8u8
);
}

impl_benchmark_test_suite!(AdminUtils, mock::new_test_ext(), mock::Test);
}
14 changes: 14 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,20 @@ pub mod pallet {

Ok(())
}

/// Sets the per-block cap on subnet epochs (dynamic tempo throttle).
#[pallet::call_index(96)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::sudo_set_max_epochs_per_block())]
pub fn sudo_set_max_epochs_per_block(
Comment thread
evgeny-s marked this conversation as resolved.
origin: OriginFor<T>,
max_epochs_per_block: u8,
) -> DispatchResult {
ensure_root(origin)?;
ensure!(max_epochs_per_block >= 1, Error::<T>::ValueNotInBounds);
pallet_subtensor::Pallet::<T>::set_max_epochs_per_block(max_epochs_per_block);

Ok(())
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 0;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -249,7 +249,7 @@ impl pallet_subtensor::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
73 changes: 73 additions & 0 deletions pallets/admin-utils/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,79 @@ fn test_sudo_set_coldkey_swap_reannouncement_delay() {
});
}

#[test]
fn test_sudo_set_max_epochs_per_block() {
new_test_ext().execute_with(|| {
let root = RuntimeOrigin::root();
let non_root = RuntimeOrigin::signed(U256::from(1));
let init_value = SubtensorModule::get_max_epochs_per_block();
let to_be_set: u8 = init_value.saturating_add(3);

// Non-root is rejected and leaves the value untouched.
assert_noop!(
AdminUtils::sudo_set_max_epochs_per_block(non_root, to_be_set),
DispatchError::BadOrigin
);
assert_eq!(SubtensorModule::get_max_epochs_per_block(), init_value);

// Zero is rejected by the `>= 1` guard (a zero cap would halt all subnet epochs).
assert_noop!(
AdminUtils::sudo_set_max_epochs_per_block(root.clone(), 0u8),
Error::<Test>::ValueNotInBounds
);
assert_eq!(SubtensorModule::get_max_epochs_per_block(), init_value);

// Root succeeds: storage is updated and the event is emitted.
assert_ok!(AdminUtils::sudo_set_max_epochs_per_block(root, to_be_set));
assert_eq!(SubtensorModule::get_max_epochs_per_block(), to_be_set);
System::assert_last_event(Event::MaxEpochsPerBlockSet(to_be_set).into());
});
}

#[test]
fn test_sudo_set_max_epochs_per_block_changes_deferrals() {
new_test_ext().execute_with(|| {
let root = RuntimeOrigin::root();

// Create several subnets and force each to be "due this block".
let created: u16 = 4;
for i in 0..created {
let netuid = NetUid::from(i + 1);
add_network(netuid, 100 /*tempo*/);
pallet_subtensor::PendingEpochAt::<Test>::insert(netuid, 1);
}

let block = SubtensorModule::get_current_block_as_u64();
let subnets: Vec<NetUid> = SubtensorModule::get_all_subnet_netuids()
.into_iter()
.filter(|x| *x != NetUid::ROOT)
.collect();
let due = subnets
.iter()
.filter(|n| SubtensorModule::should_run_epoch(**n, block))
.count();
assert!(due >= created as usize);

// Tight cap (1): every due subnet beyond the first is deferred.
assert_ok!(AdminUtils::sudo_set_max_epochs_per_block(root.clone(), 1u8));
let deferred_tight = SubtensorModule::epochs_deferred_this_block(&subnets, block).len();
assert_eq!(deferred_tight, due.saturating_sub(1));

// Raising the cap above the due count clears all deferrals — proving the
// admin-set cap directly drives which epochs are deferred.
assert_ok!(AdminUtils::sudo_set_max_epochs_per_block(
root,
(due as u8).saturating_add(2)
));
let deferred_loose = SubtensorModule::epochs_deferred_this_block(&subnets, block).len();
assert_eq!(deferred_loose, 0);
assert!(
deferred_loose < deferred_tight,
"raising MaxEpochsPerBlock must defer fewer epochs"
);
});
}

#[test]
fn test_sudo_set_dissolve_network_schedule_duration() {
new_test_ext().execute_with(|| {
Expand Down
21 changes: 21 additions & 0 deletions pallets/admin-utils/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub trait WeightInfo {
fn sudo_set_commit_reveal_weights_enabled() -> Weight;
fn sudo_set_commit_reveal_version() -> Weight;
fn sudo_set_tx_rate_limit() -> Weight;
fn sudo_set_max_epochs_per_block() -> Weight;
fn sudo_set_total_issuance() -> Weight;
fn sudo_set_rao_recycled() -> Weight;
fn sudo_set_stake_threshold() -> Weight;
Expand Down Expand Up @@ -575,6 +576,16 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
Weight::from_parts(5_500_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `SubtensorModule::MaxEpochsPerBlock` (r:0 w:1)
/// Proof: `SubtensorModule::MaxEpochsPerBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn sudo_set_max_epochs_per_block() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_000_000 picoseconds.
Weight::from_parts(4_000_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
fn sudo_set_total_issuance() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
Expand Down Expand Up @@ -1407,6 +1418,16 @@ impl WeightInfo for () {
Weight::from_parts(5_500_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `SubtensorModule::MaxEpochsPerBlock` (r:0 w:1)
/// Proof: `SubtensorModule::MaxEpochsPerBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn sudo_set_max_epochs_per_block() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_000_000 picoseconds.
Weight::from_parts(4_000_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn sudo_set_total_issuance() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
Expand Down
5 changes: 3 additions & 2 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ impl<T: Config> Pallet<T> {
/// Subnets whose epoch slot is due *this* block but is deferred by the per-block
/// cap (`MaxEpochsPerBlock`).
pub fn epochs_deferred_this_block(subnets: &[NetUid], current_block: u64) -> BTreeSet<NetUid> {
let cap = T::MaxEpochsPerBlock::get();
let cap = Self::get_max_epochs_per_block() as u32;
let mut deferred: BTreeSet<NetUid> = BTreeSet::new();
let mut epochs_run_this_block: u32 = 0;

Expand Down Expand Up @@ -353,6 +353,7 @@ impl<T: Config> Pallet<T> {
> = BTreeMap::new();
// Per-block cap on number of epochs that may run; the rest are deferred 1 block forward
// by setting `PendingEpochAt`.
let max_epochs_per_block = Self::get_max_epochs_per_block() as u32;
let mut epochs_run_this_block: u32 = 0;

for &netuid in subnets.iter() {
Expand All @@ -364,7 +365,7 @@ impl<T: Config> Pallet<T> {
}

// Per-block cap — defer if already at limit.
if epochs_run_this_block >= T::MaxEpochsPerBlock::get() {
if epochs_run_this_block >= max_epochs_per_block {
let next_block = current_block.saturating_add(1);
PendingEpochAt::<T>::insert(netuid, next_block);
Self::deposit_event(Event::EpochDeferred {
Expand Down
10 changes: 10 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,12 @@ pub mod pallet {
// T::InitialHotkeyEmissionTempo::get()
// } (DEPRECATED)

/// Default per-block epoch cap, seeded from the runtime-configured initial value.
#[pallet::type_value]
pub fn DefaultMaxEpochsPerBlock<T: Config>() -> u8 {
T::InitialMaxEpochsPerBlock::get()
}

/// Default value for rate limiting
#[pallet::type_value]
pub fn DefaultTxRateLimit<T: Config>() -> u64 {
Expand Down Expand Up @@ -2130,6 +2136,10 @@ pub mod pallet {
DefaultRAORecycledForRegistration<T>,
>;

/// --- ITEM ( max_epochs_per_block )
#[pallet::storage]
pub type MaxEpochsPerBlock<T> = StorageValue<_, u8, ValueQuery, DefaultMaxEpochsPerBlock<T>>;

/// --- ITEM ( tx_rate_limit )
#[pallet::storage]
pub type TxRateLimit<T> = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit<T>>;
Expand Down
7 changes: 4 additions & 3 deletions pallets/subtensor/src/macros/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,10 @@ mod config {
/// Burn account ID
#[pallet::constant]
type BurnAccountId: Get<PalletId>;
/// Per-block cap on number of subnet epochs that may execute in a single
/// `block_step`; the rest are deferred 1 block forward via `PendingEpochAt`.
/// Initial default per-block cap on number of subnet epochs that may
/// execute in a single `block_step`; the rest are deferred 1 block forward via
/// `PendingEpochAt`.
#[pallet::constant]
type MaxEpochsPerBlock: Get<u32>;
type InitialMaxEpochsPerBlock: Get<u8>;
}
}
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ mod events {
MaxBurnSet(NetUid, TaoBalance),
/// setting min burn on a network.
MinBurnSet(NetUid, TaoBalance),
/// setting the per-block epoch cap (dynamic tempo throttle).
MaxEpochsPerBlockSet(u8),
/// setting the transaction rate limit.
TxRateLimitSet(u64),
/// setting the delegate take transaction rate limit.
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/tests/coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4272,7 +4272,7 @@ fn test_get_subnet_terms_alpha_emissions_cap() {
#[test]
fn test_epochs_deferred_this_block_respects_cap() {
new_test_ext(1).execute_with(|| {
let cap = <Test as Config>::MaxEpochsPerBlock::get() as usize;
let cap = SubtensorModule::get_max_epochs_per_block() as usize;
let n = cap + 2;

for i in 0..n {
Expand Down Expand Up @@ -4311,7 +4311,7 @@ fn test_epochs_deferred_this_block_respects_cap() {
#[test]
fn test_reveal_crv3_defers_with_capped_epoch() {
new_test_ext(1).execute_with(|| {
let cap = <Test as Config>::MaxEpochsPerBlock::get() as usize;
let cap = SubtensorModule::get_max_epochs_per_block() as usize;
let n = cap + 2;
let mec0 = subtensor_runtime_common::MechId::from(0);

Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 10;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl crate::Config for Test {
Expand Down Expand Up @@ -341,7 +341,7 @@ impl crate::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/tests/mock_high_ed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 10;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl crate::Config for Test {
Expand Down Expand Up @@ -301,7 +301,7 @@ impl crate::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
9 changes: 9 additions & 0 deletions pallets/subtensor/src/utils/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,15 @@ impl<T: Config> Pallet<T> {
// ========= Sudo =========
// ========================

// Per-block epoch cap (dynamic tempo throttle)
pub fn get_max_epochs_per_block() -> u8 {
MaxEpochsPerBlock::<T>::get()
}
pub fn set_max_epochs_per_block(max_epochs_per_block: u8) {
MaxEpochsPerBlock::<T>::put(max_epochs_per_block);
Self::deposit_event(Event::MaxEpochsPerBlockSet(max_epochs_per_block));
}

// Configure tx rate limiting
pub fn get_tx_rate_limit() -> u64 {
TxRateLimit::<T>::get()
Expand Down
4 changes: 2 additions & 2 deletions pallets/transaction-fee/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 0;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -319,7 +319,7 @@ impl pallet_subtensor::Config for Test {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
4 changes: 2 additions & 2 deletions precompiles/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ parameter_types! {
pub const EvmKeyAssociateRateLimit: u64 = 0;
pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr");
pub const BurnAccountId: PalletId = PalletId(*b"burntnsr");
pub const MaxEpochsPerBlock: u32 = 32;
pub const MaxEpochsPerBlock: u8 = 32;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
Expand Down Expand Up @@ -483,7 +483,7 @@ impl pallet_subtensor::Config for Runtime {
type AuthorshipProvider = MockAuthorshipProvider;
type SubtensorPalletId = SubtensorPalletId;
type BurnAccountId = BurnAccountId;
type MaxEpochsPerBlock = MaxEpochsPerBlock;
type InitialMaxEpochsPerBlock = MaxEpochsPerBlock;
type WeightInfo = ();
}

Expand Down
Loading
Loading