code/common/src/proxy.rs

proxy.rs

219 lines · 6,346 bytes · 19a6485969RawGitHub
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use frame_support::traits::{Contains, GetCallIndex, GetCallName, PalletInfoAccess};
use scale_info::TypeInfo;
use sp_runtime::Vec;
use subtensor_macros::freeze_struct;

/// Shared proxy filter model exposed by the runtime API.
///
/// Runtime filtering remains the source of truth. This metadata is the client-facing
/// allowlist view of the same rules.
/// Stable proxy type identifiers used on-chain and by RPC clients.
#[derive(
    Copy,
    Clone,
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Encode,
    Decode,
    DecodeWithMemTracking,
    Debug,
    MaxEncodedLen,
    TypeInfo,
)]
pub enum ProxyType {
    Any,
    Owner,
    NonCritical,
    NonTransfer,
    Senate,
    NonFungible, // Nothing involving moving TAO
    Triumvirate,
    Governance,
    Staking,
    Registration,
    Transfer,
    SmallTransfer,
    RootWeights, // Deprecated
    ChildKeys,
    SudoUncheckedSetCode,
    SwapHotkey,
    SubnetLeaseBeneficiary,
    RootClaim,
}

impl TryFrom<u8> for ProxyType {
    type Error = ();

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Any),
            1 => Ok(Self::Owner),
            2 => Ok(Self::NonCritical),
            3 => Ok(Self::NonTransfer),
            4 => Ok(Self::Senate),
            5 => Ok(Self::NonFungible),
            6 => Ok(Self::Triumvirate),
            7 => Ok(Self::Governance),
            8 => Ok(Self::Staking),
            9 => Ok(Self::Registration),
            10 => Ok(Self::Transfer),
            11 => Ok(Self::SmallTransfer),
            12 => Ok(Self::RootWeights),
            13 => Ok(Self::ChildKeys),
            14 => Ok(Self::SudoUncheckedSetCode),
            15 => Ok(Self::SwapHotkey),
            16 => Ok(Self::SubnetLeaseBeneficiary),
            17 => Ok(Self::RootClaim),
            _ => Err(()),
        }
    }
}

impl From<ProxyType> for u8 {
    fn from(proxy_type: ProxyType) -> Self {
        match proxy_type {
            ProxyType::Any => 0,
            ProxyType::Owner => 1,
            ProxyType::NonCritical => 2,
            ProxyType::NonTransfer => 3,
            ProxyType::Senate => 4,
            ProxyType::NonFungible => 5,
            ProxyType::Triumvirate => 6,
            ProxyType::Governance => 7,
            ProxyType::Staking => 8,
            ProxyType::Registration => 9,
            ProxyType::Transfer => 10,
            ProxyType::SmallTransfer => 11,
            ProxyType::RootWeights => 12,
            ProxyType::ChildKeys => 13,
            ProxyType::SudoUncheckedSetCode => 14,
            ProxyType::SwapHotkey => 15,
            ProxyType::SubnetLeaseBeneficiary => 16,
            ProxyType::RootClaim => 17,
        }
    }
}

impl ProxyType {
    pub fn is_deprecated(&self) -> bool {
        matches!(
            self,
            Self::Triumvirate | Self::Senate | Self::Governance | Self::RootWeights
        )
    }
}

impl Default for ProxyType {
    fn default() -> Self {
        Self::Any
    }
}

/// Extra constraint attached to an allowed call.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub enum CallConstraint {
    /// The named numeric parameter must be lower than `limit`.
    ParamLessThan { param_name: Vec<u8>, limit: u128 },
    /// The named boxed call parameter must target this pallet/call pair.
    NestedCallMustBe {
        param_name: Vec<u8>,
        pallet_name: Vec<u8>,
        call_name: Vec<u8>,
    },
}

/// Runtime call identity exposed in proxy filter metadata.
#[freeze_struct("85f86877d3d9b870")]
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub struct CallInfo {
    /// Runtime pallet name.
    pub pallet_name: Vec<u8>,
    /// Runtime pallet index.
    pub pallet_index: u8,
    /// Pallet call name.
    pub call_name: Vec<u8>,
    /// Pallet call index.
    pub call_index: u8,
    /// Optional value or nested-call constraint.
    pub constraint: Option<CallConstraint>,
}

pub fn call_info_by_name<P: PalletInfoAccess, C: GetCallName + GetCallIndex>(
    name: &str,
) -> CallInfo {
    let names = C::get_call_names();
    let indices = C::get_call_indices();
    let pos = names
        .iter()
        .position(|n| *n == name)
        .unwrap_or_else(|| panic!("Call '{}' not found in pallet '{}'", name, P::name()));

    CallInfo {
        pallet_name: P::name().as_bytes().to_vec(),
        pallet_index: P::index() as u8,
        call_name: name.as_bytes().to_vec(),
        call_index: indices
            .get(pos)
            .copied()
            .unwrap_or_else(|| panic!("Call '{}' index out of bounds in '{}'", name, P::name())),
        constraint: None,
    }
}

/// Metadata view for a call filter group.
///
/// Implementations should be generated from the same rules as the executable
/// filter so clients and runtime behavior cannot drift.
pub trait CallFilterMetadata {
    fn call_infos() -> Vec<CallInfo>;
}

/// A reusable filter group: executable predicate plus metadata for clients.
pub trait CallFilterGroup<Call>: Contains<Call> + CallFilterMetadata {}

impl<T, Call> CallFilterGroup<Call> for T where T: Contains<Call> + CallFilterMetadata {}

impl CallFilterMetadata for () {
    fn call_infos() -> Vec<CallInfo> {
        Vec::new()
    }
}

#[impl_trait_for_tuples::impl_for_tuples(1, 32)]
impl CallFilterMetadata for Tuple {
    fn call_infos() -> Vec<CallInfo> {
        let mut infos = Vec::new();
        for_tuples!( #( infos.extend(Tuple::call_infos()); )* );
        infos
    }
}

/// Public metadata model for a proxy filter.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub enum FilterMode {
    /// All runtime calls are allowed.
    AllowAll,
    /// Only listed calls are allowed. An empty list means deny all.
    Allow(Vec<CallInfo>),
}

/// Runtime API response for one proxy type.
#[freeze_struct("288413f4da5ab4ee")]
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub struct ProxyFilterInfo {
    pub proxy_type: u8,
    pub name: Vec<u8>,
    pub deprecated: bool,
    pub filter_mode: FilterMode,
}

#[freeze_struct("b0cce66ed9b2451b")]
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub struct ProxyTypeInfo {
    pub name: Vec<u8>,
    pub index: u8,
    pub deprecated: bool,
}