diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 199234a30..6d7eeefd4 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -155,9 +155,22 @@ impl Pallet { let min_take = MinTake::::get(); ensure!(take >= min_take, Error::::DelegateTakeTooLow); + // Enforce the rate limit (independently on do_add_stake rate limits) + let block: u64 = Self::get_current_block_as_u64(); + ensure!( + !Self::exceeds_tx_delegate_take_rate_limit( + Self::get_last_tx_block_delegate_take(&coldkey), + block + ), + Error::::DelegateTxRateLimitExceeded + ); + // --- 4. Set the new take value. Delegates::::insert(hotkey.clone(), take); + // Set last block for rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + // --- 5. Emit the take value. log::info!( "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", @@ -241,12 +254,12 @@ impl Pallet { Error::::DelegateTxRateLimitExceeded ); - // Set last block for rate limiting - Self::set_last_tx_block_delegate_take(&coldkey, block); - // --- 6. Set the new take value. Delegates::::insert(hotkey.clone(), take); + // Set last block for rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + // --- 7. Emit the take value. log::info!( "TakeIncreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 5db439e5b..14a9e70b8 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2824,6 +2824,9 @@ fn test_can_set_min_take_ok() { u16::MAX / 10 )); + // Wait so that we can decrease take + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + // Coldkey / hotkey 0 decreases take to min assert_ok!(SubtensorModule::do_decrease_take( <::RuntimeOrigin>::signed(coldkey0), @@ -3136,6 +3139,132 @@ fn test_rate_limits_enforced_on_increase_take() { }); } +// Test rate-limiting on decrease_take + increase_take +#[test] +fn test_rate_limits_enforced_on_increase_take_after_decrease() { + new_test_ext(1).execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with max take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + pallet_subtensor::MaxTake::::get() + )); + + // Wait so that we can decrease take + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + + // Coldkey / hotkey 0 decreases take + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + SubtensorModule::get_min_take() + )); + + // Attempt to immediately increase fails due to rate limit + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + pallet_subtensor::MaxTake::::get() + ), + Err(Error::::DelegateTxRateLimitExceeded.into()) + ); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); + + // After number of blocks, take can be increased + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + + // Can increase after waiting + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + pallet_subtensor::MaxTake::::get() + )); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + pallet_subtensor::MaxTake::::get() + ); + }); +} + +// Test rate-limiting on decrease_take +#[test] +fn test_rate_limits_enforced_on_decrease_take() { + new_test_ext(1).execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with max take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + pallet_subtensor::MaxTake::::get() + )); + + // Wait so that we can decrease take first time + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + + // Coldkey / hotkey 0 decreases take + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + SubtensorModule::get_min_take() + 1 + )); + + // Attempt to immediately decrease again fails due to rate limit + assert_eq!( + SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + SubtensorModule::get_min_take() + ), + Err(Error::::DelegateTxRateLimitExceeded.into()) + ); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + 1 + ); + + // After number of blocks, take can be decreased + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + + // Can increase after waiting + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + SubtensorModule::get_min_take() + )); + assert_eq!( + SubtensorModule::get_hotkey_take(&hotkey0), + SubtensorModule::get_min_take() + ); + }); +} + // Helper function to set up a test environment fn setup_test_environment() -> (AccountId, AccountId, AccountId) { let current_coldkey = U256::from(1);