Skip to content
Open
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
6 changes: 6 additions & 0 deletions chain-extensions/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ impl pallet_subtensor::Config for Test {
type GetCommitments = ();
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = PrecompileCleanupI;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = MockAuthorshipProvider;
type WeightInfo = ();
Expand Down Expand Up @@ -463,6 +464,11 @@ impl CommitmentsInterface for CommitmentsI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct PrecompileCleanupI;
impl pallet_subtensor::PrecompileCleanupInterface for PrecompileCleanupI {
fn purge_netuid(_netuid: NetUid) {}
}

parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
Expand Down
6 changes: 6 additions & 0 deletions eco-tests/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ impl pallet_subtensor::Config for Test {
type GetCommitments = ();
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = PrecompileCleanupI;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = MockAuthorshipProvider;
type WeightInfo = ();
Expand Down Expand Up @@ -346,6 +347,11 @@ impl CommitmentsInterface for CommitmentsI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct PrecompileCleanupI;
impl pallet_subtensor::PrecompileCleanupInterface for PrecompileCleanupI {
fn purge_netuid(_netuid: NetUid) {}
}

parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
Expand Down
6 changes: 6 additions & 0 deletions pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl pallet_subtensor::Config for Test {
type GetCommitments = ();
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = PrecompileCleanupI;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = MockAuthorshipProvider;
type WeightInfo = ();
Expand Down Expand Up @@ -366,6 +367,11 @@ impl pallet_subtensor::CommitmentsInterface for CommitmentsI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct PrecompileCleanupI;
impl pallet_subtensor::PrecompileCleanupInterface for PrecompileCleanupI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct GrandpaInterfaceImpl;
impl crate::GrandpaInterface<Test> for GrandpaInterfaceImpl {
fn schedule_change(
Expand Down
22 changes: 21 additions & 1 deletion pallets/subtensor/src/coinbase/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// DEALINGS IN THE SOFTWARE.

use super::*;
use crate::CommitmentsInterface;
use crate::{CommitmentsInterface, PrecompileCleanupInterface};
use safe_math::*;
use substrate_fixed::types::{I64F64, U96F32};
use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance, Token};
Expand Down Expand Up @@ -218,6 +218,7 @@ impl<T: Config> Pallet<T> {
Self::destroy_alpha_in_out_stakes(netuid)?;
T::SwapInterface::clear_protocol_liquidity(netuid)?;
T::CommitmentsInterface::purge_netuid(netuid);
T::PrecompileCleanupInterface::purge_netuid(netuid);

// --- Remove the network
Self::remove_network(netuid);
Expand Down Expand Up @@ -282,9 +283,14 @@ impl<T: Config> Pallet<T> {
Kappa::<T>::remove(netuid);
Difficulty::<T>::remove(netuid);
MaxAllowedUids::<T>::remove(netuid);
MinAllowedUids::<T>::remove(netuid);
ImmunityPeriod::<T>::remove(netuid);
ActivityCutoff::<T>::remove(netuid);
MinAllowedWeights::<T>::remove(netuid);
MaxWeightsLimit::<T>::remove(netuid);
AdjustmentAlpha::<T>::remove(netuid);
AdjustmentInterval::<T>::remove(netuid);
MinNonImmuneUids::<T>::remove(netuid);
RegistrationsThisInterval::<T>::remove(netuid);
POWRegistrationsThisInterval::<T>::remove(netuid);
BurnRegistrationsThisInterval::<T>::remove(netuid);
Expand All @@ -300,6 +306,11 @@ impl<T: Config> Pallet<T> {
SubnetEmaTaoFlow::<T>::remove(netuid);
SubnetTaoProvided::<T>::remove(netuid);

// --- 12. Root / emission split parameters.
RootProp::<T>::remove(netuid);
RecycleOrBurn::<T>::remove(netuid);
RootClaimableThreshold::<T>::remove(netuid);

// --- 13. Token / mechanism / registration toggles.
TokenSymbol::<T>::remove(netuid);
SubnetMechanism::<T>::remove(netuid);
Expand Down Expand Up @@ -362,12 +373,21 @@ impl<T: Config> Pallet<T> {
StakeWeight::<T>::remove(netuid);
LoadedEmission::<T>::remove(netuid);

// --- 18b. Voting power.
let _ = VotingPower::<T>::clear_prefix(netuid, u32::MAX, None);
VotingPowerTrackingEnabled::<T>::remove(netuid);
VotingPowerDisableAtBlock::<T>::remove(netuid);
VotingPowerEmaAlpha::<T>::remove(netuid);

// --- 19. DMAPs where netuid is the FIRST key: clear by prefix.
let _ = BlockAtRegistration::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = Axons::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = NeuronCertificates::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = Prometheus::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = AlphaDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = RootAlphaDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = VotingPower::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = RootClaimed::<T>::clear_prefix((netuid,), u32::MAX, None);
let _ = PendingChildKeys::<T>::clear_prefix(netuid, u32::MAX, None);
let _ = AssociatedEvmAddress::<T>::clear_prefix(netuid, u32::MAX, None);

Expand Down
5 changes: 5 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2747,3 +2747,8 @@ impl<T> ProxyInterface<T> for () {
pub trait CommitmentsInterface {
fn purge_netuid(netuid: NetUid);
}

/// EVM precompile crates implement this to clean up storage when a subnet is removed.
pub trait PrecompileCleanupInterface {
fn purge_netuid(netuid: NetUid);
}
5 changes: 4 additions & 1 deletion pallets/subtensor/src/macros/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use frame_support::pallet_macros::pallet_section;
#[pallet_section]
mod config {

use crate::{CommitmentsInterface, GetAlphaForTao, GetTaoForAlpha};
use crate::{CommitmentsInterface, GetAlphaForTao, GetTaoForAlpha, PrecompileCleanupInterface};
use pallet_commitments::GetCommitments;
use subtensor_runtime_common::AuthorshipInfo;
use subtensor_swap_interface::{SwapEngine, SwapHandler};
Expand Down Expand Up @@ -60,6 +60,9 @@ mod config {
/// Interface to clean commitments on network dissolution.
type CommitmentsInterface: CommitmentsInterface;

/// Interface to clean EVM precompile storage on network dissolution.
type PrecompileCleanupInterface: PrecompileCleanupInterface;

/// Rate limit for associating an EVM key.
type EvmKeyAssociateRateLimit: Get<u64>;

Expand Down
6 changes: 6 additions & 0 deletions pallets/subtensor/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl crate::Config for Test {
type GetCommitments = ();
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = PrecompileCleanupI;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = MockAuthorshipProvider;
type WeightInfo = ();
Expand Down Expand Up @@ -361,6 +362,11 @@ impl CommitmentsInterface for CommitmentsI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct PrecompileCleanupI;
impl PrecompileCleanupInterface for PrecompileCleanupI {
fn purge_netuid(_netuid: NetUid) {}
}

parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
Expand Down
73 changes: 73 additions & 0 deletions pallets/subtensor/src/tests/networks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2632,3 +2632,76 @@ fn register_network_non_associated_hotkey_does_not_withdraw_or_write_owner_alpha
);
});
}

#[test]
fn dissolve_network_clears_root_alpha_dividends() {
new_test_ext(0).execute_with(|| {
let cold = U256::from(1);
let hot = U256::from(2);
let net = add_dynamic_network(&hot, &cold);

// Insert some root alpha dividend data for this subnet.
RootAlphaDividendsPerSubnet::<Test>::insert(net, hot, AlphaBalance::from(1000));
assert!(RootAlphaDividendsPerSubnet::<Test>::contains_key(net, hot));

// Dissolve the network.
SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0));
SubnetTAO::<Test>::insert(net, TaoBalance::from(0));
Emission::<Test>::insert(net, Vec::<AlphaBalance>::new());
assert_ok!(SubtensorModule::do_dissolve_network(net));

// RootAlphaDividendsPerSubnet should be cleared.
assert!(
!RootAlphaDividendsPerSubnet::<Test>::contains_key(net, hot),
"RootAlphaDividendsPerSubnet not cleaned up after network removal"
);
});
}

#[test]
fn dissolve_network_clears_voting_power() {
new_test_ext(0).execute_with(|| {
let cold = U256::from(1);
let hot = U256::from(2);
let net = add_dynamic_network(&hot, &cold);

// Insert voting power for a validator on this subnet.
VotingPower::<Test>::insert(net, hot, 9999u64);
assert!(VotingPower::<Test>::contains_key(net, hot));

// Dissolve the network.
SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0));
SubnetTAO::<Test>::insert(net, TaoBalance::from(0));
Emission::<Test>::insert(net, Vec::<AlphaBalance>::new());
assert_ok!(SubtensorModule::do_dissolve_network(net));

assert!(
!VotingPower::<Test>::contains_key(net, hot),
"VotingPower not cleaned up after network removal"
);
});
}

#[test]
fn dissolve_network_clears_root_claimed() {
new_test_ext(0).execute_with(|| {
let cold = U256::from(1);
let hot = U256::from(2);
let net = add_dynamic_network(&hot, &cold);

// Simulate a recorded root claim for this subnet.
RootClaimed::<Test>::insert((net, hot, cold), 500u128);
assert!(RootClaimed::<Test>::contains_key((net, hot, cold)));

// Dissolve the network.
SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0));
SubnetTAO::<Test>::insert(net, TaoBalance::from(0));
Emission::<Test>::insert(net, Vec::<AlphaBalance>::new());
assert_ok!(SubtensorModule::do_dissolve_network(net));

assert!(
!RootClaimed::<Test>::contains_key((net, hot, cold)),
"RootClaimed not cleaned up after network removal"
);
});
}
6 changes: 6 additions & 0 deletions pallets/transaction-fee/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ impl pallet_subtensor::Config for Test {
type GetCommitments = ();
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = PrecompileCleanupI;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = MockAuthorshipProvider;
type WeightInfo = ();
Expand Down Expand Up @@ -438,6 +439,11 @@ impl pallet_subtensor::CommitmentsInterface for CommitmentsI {
fn purge_netuid(_netuid: NetUid) {}
}

pub struct PrecompileCleanupI;
impl pallet_subtensor::PrecompileCleanupInterface for PrecompileCleanupI {
fn purge_netuid(_netuid: NetUid) {}
}

parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
Expand Down
14 changes: 12 additions & 2 deletions precompiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub use metagraph::MetagraphPrecompile;
pub use neuron::NeuronPrecompile;
pub use proxy::ProxyPrecompile;
pub use sr25519::Sr25519Verify;
pub use staking::{StakingPrecompile, StakingPrecompileV2};
pub use staking::{StakingPrecompile, StakingPrecompileV2, purge_netuid_allowances};
pub use storage_query::StorageQueryPrecompile;
pub use subnet::SubnetPrecompile;
pub use uid_lookup::UidLookupPrecompile;
Expand All @@ -55,7 +55,7 @@ mod metagraph;
mod neuron;
mod proxy;
mod sr25519;
mod staking;
pub mod staking;
mod storage_query;
mod subnet;
mod uid_lookup;
Expand Down Expand Up @@ -285,6 +285,16 @@ where
}
}

/// Implementation of [`pallet_subtensor::PrecompileCleanupInterface`] that cleans up
/// EVM precompile storage (e.g. staking allowances) when a subnet is deregistered.
pub struct PrecompileCleanup;

impl pallet_subtensor::PrecompileCleanupInterface for PrecompileCleanup {
fn purge_netuid(netuid: subtensor_runtime_common::NetUid) {
purge_netuid_allowances(netuid.into());
}
}

fn hash(a: u64) -> H160 {
H160::from_low_u64_be(a)
}
Expand Down
45 changes: 45 additions & 0 deletions precompiles/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ pub type AllowancesStorage = StorageDoubleMap<
ValueQuery,
>;

/// Remove all AllowancesStorage entries whose key contains the given netuid.
pub fn purge_netuid_allowances(netuid: u16) {
let to_remove: Vec<(H160, (H160, u16))> = AllowancesStorage::iter()
.filter_map(|(approver, (spender, n), _)| {
if n == netuid {
Some((approver, (spender, n)))
} else {
None
}
})
.collect();
for (approver, key) in to_remove {
AllowancesStorage::remove(approver, key);
}
}

// Old StakingPrecompile had ETH-precision in values, which was not alligned with Substrate API. So
// it's kinda deprecated, but exists for backward compatibility. Eventually, we should remove it
// to stop supporting both precompiles.
Expand Down Expand Up @@ -895,3 +911,32 @@ fn try_u64_from_u256(value: U256) -> Result<u64, PrecompileFailure> {
exit_status: ExitError::Other("the value is outside of u64 bounds".into()),
})
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn purge_netuid_allowances_removes_only_target_netuid() {
sp_io::TestExternalities::default().execute_with(|| {
let approver = H160::from_low_u64_be(1);
let spender = H160::from_low_u64_be(2);
let netuid_a: u16 = 5;
let netuid_b: u16 = 7;

AllowancesStorage::insert(approver, (spender, netuid_a), U256::from(100));
AllowancesStorage::insert(approver, (spender, netuid_b), U256::from(200));

purge_netuid_allowances(netuid_a);

assert_eq!(
AllowancesStorage::get(approver, (spender, netuid_a)),
U256::zero(),
);
assert_eq!(
AllowancesStorage::get(approver, (spender, netuid_b)),
U256::from(200),
);
});
}
}
1 change: 1 addition & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,7 @@ impl pallet_subtensor::Config for Runtime {
type GetCommitments = GetCommitmentsStruct;
type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage;
type CommitmentsInterface = CommitmentsI;
type PrecompileCleanupInterface = subtensor_precompiles::PrecompileCleanup;
type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit;
type AuthorshipProvider = BlockAuthorFromAura<Aura>;
type WeightInfo = pallet_subtensor::weights::SubstrateWeight<Runtime>;
Expand Down
Loading