Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 48519ac

Browse files
committedMay 29, 2025·
Merge branch 'devnet-ready' into allow-ip-as-0
2 parents f27d155 + 17f32d3 commit 48519ac

File tree

10 files changed

+253
-18
lines changed

10 files changed

+253
-18
lines changed
 

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ arithmetic-side-effects = "deny"
3939
type_complexity = "allow"
4040
unwrap-used = "deny"
4141
manual_inspect = "allow"
42+
result_large_err = "allow"
4243
useless_conversion = "allow" # until polkadot is patched
4344

4445
[workspace.dependencies]

‎pallets/drand/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ pub mod pallet {
288288
pulses_payload: payload,
289289
signature,
290290
} => {
291+
// Blacklist stale pulses in the txpool that can stall finalization.
292+
if payload.block_number < BlockNumberFor::<T>::from(5612500u32) {
293+
return InvalidTransaction::Stale.into();
294+
}
291295
let signature = signature.as_ref().ok_or(InvalidTransaction::BadSigner)?;
292296
Self::validate_signature_and_parameters(
293297
payload,

‎pallets/drand/src/tests.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn it_can_submit_valid_pulse_when_beacon_config_exists() {
4747
let p: Pulse = u_p.try_into_pulse().unwrap();
4848

4949
let alice = sp_keyring::Sr25519Keyring::Alice;
50-
let block_number = 1;
50+
let block_number = 100_000_000;
5151
System::set_block_number(block_number);
5252

5353
// Set the beacon config
@@ -91,7 +91,7 @@ fn it_can_submit_valid_pulse_when_beacon_config_exists() {
9191
fn it_rejects_invalid_pulse_due_to_bad_signature() {
9292
new_test_ext().execute_with(|| {
9393
let alice = sp_keyring::Sr25519Keyring::Alice;
94-
let block_number = 1;
94+
let block_number = 100_000_000;
9595
System::set_block_number(block_number);
9696

9797
// Set the beacon config using Root origin
@@ -139,7 +139,7 @@ fn it_rejects_invalid_pulse_due_to_bad_signature() {
139139
#[test]
140140
fn it_rejects_pulses_with_non_incremental_round_numbers() {
141141
new_test_ext().execute_with(|| {
142-
let block_number = 1;
142+
let block_number = 100_000_000;
143143
let alice = sp_keyring::Sr25519Keyring::Alice;
144144
System::set_block_number(block_number);
145145

@@ -189,7 +189,7 @@ fn it_rejects_pulses_with_non_incremental_round_numbers() {
189189
#[test]
190190
fn it_blocks_non_root_from_submit_beacon_info() {
191191
new_test_ext().execute_with(|| {
192-
let block_number = 1;
192+
let block_number = 100_000_000;
193193
let alice = sp_keyring::Sr25519Keyring::Alice;
194194
System::set_block_number(block_number);
195195

@@ -239,7 +239,7 @@ fn it_blocks_non_root_from_submit_beacon_info() {
239239
#[test]
240240
fn signed_cannot_submit_beacon_info() {
241241
new_test_ext().execute_with(|| {
242-
let block_number = 1;
242+
let block_number = 100_000_000;
243243
let alice = sp_keyring::Sr25519Keyring::Alice;
244244
System::set_block_number(block_number);
245245

@@ -268,7 +268,7 @@ fn signed_cannot_submit_beacon_info() {
268268
#[test]
269269
fn test_validate_unsigned_write_pulse() {
270270
new_test_ext().execute_with(|| {
271-
let block_number = 1;
271+
let block_number = 100_000_000;
272272
let alice = sp_keyring::Sr25519Keyring::Alice;
273273
System::set_block_number(block_number);
274274
let pulses_payload = PulsesPayload {
@@ -293,7 +293,7 @@ fn test_validate_unsigned_write_pulse() {
293293
#[test]
294294
fn test_not_validate_unsigned_write_pulse_with_bad_proof() {
295295
new_test_ext().execute_with(|| {
296-
let block_number = 1;
296+
let block_number = 100_000_000;
297297
let alice = sp_keyring::Sr25519Keyring::Alice;
298298
System::set_block_number(block_number);
299299
let pulses_payload = PulsesPayload {
@@ -319,7 +319,7 @@ fn test_not_validate_unsigned_write_pulse_with_bad_proof() {
319319
#[test]
320320
fn test_not_validate_unsigned_write_pulse_with_no_payload_signature() {
321321
new_test_ext().execute_with(|| {
322-
let block_number = 1;
322+
let block_number = 100_000_000;
323323
let alice = sp_keyring::Sr25519Keyring::Alice;
324324
System::set_block_number(block_number);
325325
let pulses_payload = PulsesPayload {
@@ -388,8 +388,8 @@ fn can_execute_and_handle_valid_http_responses() {
388388
#[test]
389389
fn validate_unsigned_rejects_future_block_number() {
390390
new_test_ext().execute_with(|| {
391-
let block_number = 1;
392-
let future_block_number = 100;
391+
let block_number = 100_000_000;
392+
let future_block_number = 100_000_100;
393393
let alice = sp_keyring::Sr25519Keyring::Alice;
394394
System::set_block_number(block_number);
395395
let pulses_payload = PulsesPayload {

‎pallets/subtensor/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,20 @@ where
23002300
// Self::get_priority_staking(who, hotkey, *amount_unstaked),
23012301
// )
23022302
// }
2303+
Some(Call::unstake_all { hotkey }) => {
2304+
// Fully validate the user input
2305+
Self::result_to_validity(
2306+
Pallet::<T>::validate_unstake_all(who, hotkey, false),
2307+
Self::get_priority_vanilla(),
2308+
)
2309+
}
2310+
Some(Call::unstake_all_alpha { hotkey }) => {
2311+
// Fully validate the user input
2312+
Self::result_to_validity(
2313+
Pallet::<T>::validate_unstake_all(who, hotkey, true),
2314+
Self::get_priority_vanilla(),
2315+
)
2316+
}
23032317
Some(Call::move_stake {
23042318
origin_hotkey,
23052319
destination_hotkey,

‎pallets/subtensor/src/staking/stake_utils.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,42 @@ impl<T: Config> Pallet<T> {
995995
Ok(())
996996
}
997997

998+
/// Validate if unstake_all can be executed
999+
///
1000+
pub fn validate_unstake_all(
1001+
coldkey: &T::AccountId,
1002+
hotkey: &T::AccountId,
1003+
only_alpha: bool,
1004+
) -> Result<(), Error<T>> {
1005+
// Get all netuids (filter out root)
1006+
let subnets: Vec<u16> = Self::get_all_subnet_netuids();
1007+
1008+
// Ensure that the hotkey account exists this is only possible through registration.
1009+
ensure!(
1010+
Self::hotkey_account_exists(hotkey),
1011+
Error::<T>::HotKeyAccountNotExists
1012+
);
1013+
1014+
let mut unstaking_any = false;
1015+
for netuid in subnets.iter() {
1016+
if only_alpha && (*netuid == Self::get_root_netuid()) {
1017+
continue;
1018+
}
1019+
1020+
// Get user's stake in this subnet
1021+
let alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, *netuid);
1022+
1023+
if Self::validate_remove_stake(coldkey, hotkey, *netuid, alpha, alpha, false).is_ok() {
1024+
unstaking_any = true;
1025+
}
1026+
}
1027+
1028+
// If no unstaking happens, return error
1029+
ensure!(unstaking_any, Error::<T>::AmountTooLow);
1030+
1031+
Ok(())
1032+
}
1033+
9981034
/// Validate stake transition user input
9991035
/// That works for move_stake, transfer_stake, and swap_stake
10001036
///

‎pallets/subtensor/src/tests/batch_tx.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use super::mock::*;
2-
use frame_support::{assert_ok, traits::Currency};
2+
use frame_support::{
3+
assert_ok,
4+
traits::{Contains, Currency},
5+
};
36
use frame_system::Config;
47
use sp_core::U256;
58

@@ -33,3 +36,84 @@ fn test_batch_txs() {
3336
assert_eq!(Balances::total_balance(&charlie), 2_000_000_000);
3437
});
3538
}
39+
40+
#[test]
41+
fn test_cant_nest_batch_txs() {
42+
let bob = U256::from(1);
43+
let charlie = U256::from(2);
44+
45+
new_test_ext(1).execute_with(|| {
46+
let call = RuntimeCall::Utility(pallet_utility::Call::batch {
47+
calls: vec![
48+
RuntimeCall::Balances(BalanceCall::transfer_allow_death {
49+
dest: bob,
50+
value: 1_000_000_000,
51+
}),
52+
RuntimeCall::Utility(pallet_utility::Call::batch {
53+
calls: vec![RuntimeCall::Balances(BalanceCall::transfer_allow_death {
54+
dest: charlie,
55+
value: 1_000_000_000,
56+
})],
57+
}),
58+
],
59+
});
60+
61+
assert!(!<Test as Config>::BaseCallFilter::contains(&call));
62+
});
63+
}
64+
65+
#[test]
66+
fn test_can_batch_txs() {
67+
let bob = U256::from(1);
68+
69+
new_test_ext(1).execute_with(|| {
70+
let call = RuntimeCall::Utility(pallet_utility::Call::batch {
71+
calls: vec![RuntimeCall::Balances(BalanceCall::transfer_allow_death {
72+
dest: bob,
73+
value: 1_000_000_000,
74+
})],
75+
});
76+
77+
assert!(<Test as Config>::BaseCallFilter::contains(&call));
78+
});
79+
}
80+
81+
#[test]
82+
fn test_cant_nest_batch_diff_batch_txs() {
83+
let charlie = U256::from(2);
84+
85+
new_test_ext(1).execute_with(|| {
86+
let call = RuntimeCall::Utility(pallet_utility::Call::batch {
87+
calls: vec![RuntimeCall::Utility(pallet_utility::Call::force_batch {
88+
calls: vec![RuntimeCall::Balances(BalanceCall::transfer_allow_death {
89+
dest: charlie,
90+
value: 1_000_000_000,
91+
})],
92+
})],
93+
});
94+
95+
assert!(!<Test as Config>::BaseCallFilter::contains(&call));
96+
97+
let call2 = RuntimeCall::Utility(pallet_utility::Call::batch_all {
98+
calls: vec![RuntimeCall::Utility(pallet_utility::Call::batch {
99+
calls: vec![RuntimeCall::Balances(BalanceCall::transfer_allow_death {
100+
dest: charlie,
101+
value: 1_000_000_000,
102+
})],
103+
})],
104+
});
105+
106+
assert!(!<Test as Config>::BaseCallFilter::contains(&call2));
107+
108+
let call3 = RuntimeCall::Utility(pallet_utility::Call::force_batch {
109+
calls: vec![RuntimeCall::Utility(pallet_utility::Call::batch_all {
110+
calls: vec![RuntimeCall::Balances(BalanceCall::transfer_allow_death {
111+
dest: charlie,
112+
value: 1_000_000_000,
113+
})],
114+
})],
115+
});
116+
117+
assert!(!<Test as Config>::BaseCallFilter::contains(&call3));
118+
});
119+
}

‎pallets/subtensor/src/tests/mock.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
use crate::utils::rate_limiting::TransactionType;
33
use frame_support::derive_impl;
44
use frame_support::dispatch::DispatchResultWithPostInfo;
5+
use frame_support::traits::{Contains, Everything, InsideBoth};
56
use frame_support::weights::Weight;
67
use frame_support::weights::constants::RocksDbWeight;
78
use frame_support::{
89
assert_ok, parameter_types,
9-
traits::{Everything, Hooks, PrivilegeCmp},
10+
traits::{Hooks, PrivilegeCmp},
1011
};
1112
use frame_system as system;
1213
use frame_system::{EnsureNever, EnsureRoot, RawOrigin, limits};
@@ -88,9 +89,31 @@ impl pallet_balances::Config for Test {
8889
type MaxFreezes = ();
8990
}
9091

92+
pub struct NoNestingCallFilter;
93+
94+
impl Contains<RuntimeCall> for NoNestingCallFilter {
95+
fn contains(call: &RuntimeCall) -> bool {
96+
match call {
97+
RuntimeCall::Utility(inner) => {
98+
let calls = match inner {
99+
pallet_utility::Call::force_batch { calls } => calls,
100+
pallet_utility::Call::batch { calls } => calls,
101+
pallet_utility::Call::batch_all { calls } => calls,
102+
_ => &Vec::new(),
103+
};
104+
105+
!calls.iter().any(|call| {
106+
matches!(call, RuntimeCall::Utility(inner) if matches!(inner, pallet_utility::Call::force_batch { .. } | pallet_utility::Call::batch_all { .. } | pallet_utility::Call::batch { .. }))
107+
})
108+
}
109+
_ => true,
110+
}
111+
}
112+
}
113+
91114
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
92115
impl system::Config for Test {
93-
type BaseCallFilter = Everything;
116+
type BaseCallFilter = InsideBoth<Everything, NoNestingCallFilter>;
94117
type BlockWeights = BlockWeights;
95118
type BlockLength = ();
96119
type DbWeight = RocksDbWeight;

‎pallets/subtensor/src/tests/staking.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3841,6 +3841,57 @@ fn test_unstake_low_liquidity_validate() {
38413841
});
38423842
}
38433843

3844+
#[test]
3845+
fn test_unstake_all_validate() {
3846+
// Testing the signed extension validate function
3847+
// correctly filters the `unstake_all` transaction.
3848+
3849+
new_test_ext(0).execute_with(|| {
3850+
let subnet_owner_coldkey = U256::from(1001);
3851+
let subnet_owner_hotkey = U256::from(1002);
3852+
let hotkey = U256::from(2);
3853+
let coldkey = U256::from(3);
3854+
let amount_staked = DefaultMinStake::<Test>::get() * 10 + DefaultStakingFee::<Test>::get();
3855+
3856+
let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
3857+
SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey);
3858+
SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked);
3859+
3860+
// Simulate stake for hotkey
3861+
SubnetTAO::<Test>::insert(netuid, u64::MAX / 1000);
3862+
SubnetAlphaIn::<Test>::insert(netuid, u64::MAX / 1000);
3863+
SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, amount_staked, 0);
3864+
3865+
// Set the liquidity at lowest possible value so that all staking requests fail
3866+
SubnetTAO::<Test>::insert(
3867+
netuid,
3868+
DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(),
3869+
);
3870+
SubnetAlphaIn::<Test>::insert(
3871+
netuid,
3872+
DefaultMinimumPoolLiquidity::<Test>::get().to_num::<u64>(),
3873+
);
3874+
3875+
// unstake_all call
3876+
let call = RuntimeCall::SubtensorModule(SubtensorCall::unstake_all { hotkey });
3877+
3878+
let info: DispatchInfo =
3879+
DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default();
3880+
3881+
let extension = SubtensorSignedExtension::<Test>::new();
3882+
// Submit to the signed extension validate function
3883+
let result_no_stake = extension.validate(&coldkey, &call.clone(), &info, 10);
3884+
3885+
// Should fail due to insufficient stake
3886+
assert_err!(
3887+
result_no_stake,
3888+
TransactionValidityError::Invalid(InvalidTransaction::Custom(
3889+
CustomTransactionError::StakeAmountTooLow.into()
3890+
))
3891+
);
3892+
});
3893+
}
3894+
38443895
#[test]
38453896
fn test_max_amount_add_root() {
38463897
new_test_ext(0).execute_with(|| {

0 commit comments

Comments
 (0)