diff --git a/common/src/proxy.rs b/common/src/proxy.rs index f40b3f2076..8a7287cade 100644 --- a/common/src/proxy.rs +++ b/common/src/proxy.rs @@ -36,7 +36,7 @@ pub enum ProxyType { Registration, Transfer, SmallTransfer, - RootWeights, // Deprecated + RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, @@ -99,10 +99,7 @@ impl From for u8 { impl ProxyType { pub fn is_deprecated(&self) -> bool { - matches!( - self, - Self::Triumvirate | Self::Senate | Self::Governance | Self::RootWeights - ) + matches!(self, Self::Triumvirate | Self::Senate | Self::Governance) } } diff --git a/docs/tx/add-proxy.mdx b/docs/tx/add-proxy.mdx index 571fb0f031..037d272e82 100644 --- a/docs/tx/add-proxy.mdx +++ b/docs/tx/add-proxy.mdx @@ -21,7 +21,7 @@ only to keys you control or fully trust. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `delegate_ss58` | string | yes | Key that will be allowed to sign for this account. | -| `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | +| `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, and Governance are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. RootWeights covers exactly SubtensorModule.set_root_weights. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | | `delay` | integer | no | Announcement delay in blocks: the delegate must announce each call and wait this long before executing it, giving you time to veto. 0 executes immediately. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 diff --git a/docs/tx/create-pure-proxy.mdx b/docs/tx/create-pure-proxy.mdx index e603cd6c91..e180488881 100644 --- a/docs/tx/create-pure-proxy.mdx +++ b/docs/tx/create-pure-proxy.mdx @@ -21,7 +21,7 @@ the pure proxy and anything it holds. | Parameter | Type | Required | Description | | --- | --- | --- | --- | -| `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | +| `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, and Governance are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. RootWeights covers exactly SubtensorModule.set_root_weights. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | | `delay` | integer | no | Announcement delay in blocks: the delegate must announce each call and wait this long before executing it, giving you time to veto. 0 executes immediately. | | `index` | integer | no | Disambiguator so one signer can create several pure proxies in one block; also part of the derived address. Keep 0 unless batching. | diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 20b798f72c..f4ad13bf81 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -235,7 +235,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 441, + spec_version: 442, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/src/proxy_filters/call_groups.rs b/runtime/src/proxy_filters/call_groups.rs index 4575ab9b7a..85380a1fff 100644 --- a/runtime/src/proxy_filters/call_groups.rs +++ b/runtime/src/proxy_filters/call_groups.rs @@ -630,6 +630,17 @@ call_filter_group!(SudoSetCodeCalls, [ where nested(call) == RuntimeCall::System(SystemCall::set_code), ]); +// `RootWeights`: exactly the root-weight vector call. Deliberately not the +// whole `SubtensorCommonCalls` group it overlaps: the delegator hands out the +// right to publish their root weights, not the validator's consensus-weight +// surface (`set_weights`, batch/commit/reveal) that lives alongside it. +call_filter_group!( + RootWeightCalls, + [RuntimeCall::SubtensorModule( + SubtensorCall::set_root_weights + ),] +); + // Full inventory of every runtime call, used only by the coverage test that // checks it against `RuntimeCall` metadata. Nested in three blocks so the // flattened tuple stays within the `CallFilterMetadata` tuple-impl arity; diff --git a/runtime/src/proxy_filters/mod.rs b/runtime/src/proxy_filters/mod.rs index 7ec3925aa4..f3484858a1 100644 --- a/runtime/src/proxy_filters/mod.rs +++ b/runtime/src/proxy_filters/mod.rs @@ -117,11 +117,9 @@ pub(crate) fn proxy_type_filter(proxy_type: &ProxyType, call: &RuntimeCall) -> b ProxyType::SwapHotkey => HotkeySwapCalls::contains(call), ProxyType::SubnetLeaseBeneficiary => SubnetLeaseAllowed::contains(call), ProxyType::RootClaim => RootClaimCalls::contains(call), + ProxyType::RootWeights => RootWeightCalls::contains(call), ProxyType::SudoUncheckedSetCode => SudoSetCodeCalls::contains(call), - ProxyType::Triumvirate - | ProxyType::Senate - | ProxyType::Governance - | ProxyType::RootWeights => false, + ProxyType::Triumvirate | ProxyType::Senate | ProxyType::Governance => false, } } @@ -154,6 +152,10 @@ impl InstanceFilter for ProxyType { | ProxyType::SubnetLeaseBeneficiary | ProxyType::RootClaim, ) => true, + // `NonFungible` already allows `set_root_weights` (via + // `SubtensorCommonCalls`), so it may also manage the + // strictly-narrower `RootWeights` delegation. + (ProxyType::NonFungible, ProxyType::RootWeights) => true, (ProxyType::Transfer, ProxyType::SmallTransfer) => true, _ => false, } @@ -183,11 +185,11 @@ fn proxy_filter_mode(proxy_type: ProxyType) -> FilterMode { ProxyType::SwapHotkey => FilterMode::Allow(HotkeySwapCalls::call_infos()), ProxyType::SubnetLeaseBeneficiary => FilterMode::Allow(SubnetLeaseAllowed::call_infos()), ProxyType::RootClaim => FilterMode::Allow(RootClaimCalls::call_infos()), + ProxyType::RootWeights => FilterMode::Allow(RootWeightCalls::call_infos()), ProxyType::SudoUncheckedSetCode => FilterMode::Allow(SudoSetCodeCalls::call_infos()), - ProxyType::Triumvirate - | ProxyType::Senate - | ProxyType::Governance - | ProxyType::RootWeights => FilterMode::Allow(Vec::new()), + ProxyType::Triumvirate | ProxyType::Senate | ProxyType::Governance => { + FilterMode::Allow(Vec::new()) + } } } @@ -291,12 +293,70 @@ mod tests { ProxyType::Triumvirate, ProxyType::Senate, ProxyType::Governance, - ProxyType::RootWeights, ] { assert!(allowed_calls(deprecated).is_empty()); } } + // Re-enabled `RootWeights` is the narrowest weight grant: the root-weight + // vector call and nothing else — not consensus weights, stake movement, + // key rotation, or child keys. + #[test] + fn root_weights_grants_exactly_set_root_weights() { + use pallet_subtensor::Call as SubtensorCall; + use subtensor_runtime_common::{AccountId, AlphaBalance, NetUid, TaoBalance}; + + let hotkey = AccountId::new([1u8; 32]); + + let set_root_weights = RuntimeCall::SubtensorModule(SubtensorCall::set_root_weights { + dests: vec![0], + weights: vec![0], + }); + assert!(proxy_type_filter( + &ProxyType::RootWeights, + &set_root_weights + )); + + let denied = [ + RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid: NetUid::from(1), + dests: vec![0], + weights: vec![0], + version_key: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey: hotkey.clone(), + netuid: NetUid::from(1), + amount_staked: TaoBalance::from(1), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey: hotkey.clone(), + netuid: NetUid::from(1), + amount_unstaked: AlphaBalance::from(1), + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_hotkey { + hotkey: hotkey.clone(), + new_hotkey: AccountId::new([2u8; 32]), + netuid: None, + }), + RuntimeCall::SubtensorModule(SubtensorCall::set_children { + hotkey, + netuid: NetUid::from(1), + children: vec![], + }), + ]; + for call in &denied { + assert!( + !proxy_type_filter(&ProxyType::RootWeights, call), + "RootWeights must not allow {:?}", + call + ); + } + + // The metadata view agrees: exactly one allowed call. + assert_eq!(allowed_calls(ProxyType::RootWeights).len(), 1); + } + // Broad proxies are specified subtractively here (all calls minus a few // denied groups) and checked against the additive composition in the filter. // Because the inventory groups partition every runtime call, the two must @@ -405,6 +465,19 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn non_fungible_superset_is_explicit_allowlist() { + let actual = all_proxy_types() + .into_iter() + .filter(|proxy_type| ProxyType::NonFungible.is_superset(proxy_type)) + .collect::>(); + let expected = [ProxyType::NonFungible, ProxyType::RootWeights] + .into_iter() + .collect::>(); + + assert_eq!(actual, expected); + } + #[test] fn owner_allows_only_owner_settable_config() { let owner = allowed_calls(ProxyType::Owner); @@ -513,6 +586,10 @@ mod tests { "SubtensorModule::claim_root_with_hotkey", ]) ); + assert_eq!( + allowed_calls(ProxyType::RootWeights), + expected(&["SubtensorModule::set_root_weights"]) + ); assert_eq!( allowed_calls(ProxyType::SudoUncheckedSetCode), expected(&["Sudo::sudo_unchecked_weight"]) diff --git a/sdk/python/bittensor/intents/proxy.py b/sdk/python/bittensor/intents/proxy.py index ddb8018a59..ed33158b87 100644 --- a/sdk/python/bittensor/intents/proxy.py +++ b/sdk/python/bittensor/intents/proxy.py @@ -56,10 +56,11 @@ def check_proxy_type(proxy_type: str) -> str: PROXY_TYPE_HELP = ( "Scope of calls the delegation covers. One of: " + ", ".join(PROXY_TYPES) - + ". Triumvirate, Senate, Governance, and RootWeights are deprecated on " - "the current runtime: they deny all calls, so a proxy of those types can " - "dispatch nothing. Prefer the narrowest type that covers your use; Any " - "can do everything the account can, including transfers." + + ". Triumvirate, Senate, and Governance are deprecated on the current " + "runtime: they deny all calls, so a proxy of those types can dispatch " + "nothing. RootWeights covers exactly SubtensorModule.set_root_weights. " + "Prefer the narrowest type that covers your use; Any can do everything " + "the account can, including transfers." ) DELAY_HELP = ( diff --git a/website/apps/bittensor-website/public/catalog/intents.json b/website/apps/bittensor-website/public/catalog/intents.json index 754b2c7671..fd0d819100 100644 --- a/website/apps/bittensor-website/public/catalog/intents.json +++ b/website/apps/bittensor-website/public/catalog/intents.json @@ -86,7 +86,7 @@ }, "proxy_type": { "type": "string", - "description": "Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers." + "description": "Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, and Governance are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. RootWeights covers exactly SubtensorModule.set_root_weights. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers." }, "delay": { "type": "integer", @@ -847,7 +847,7 @@ "properties": { "proxy_type": { "type": "string", - "description": "Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers." + "description": "Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, and Governance are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. RootWeights covers exactly SubtensorModule.set_root_weights. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers." }, "delay": { "type": "integer",