기판 팔레트 코드 분석: Sudo

개요



기판 기반 체인에는 sudo 계정이라는 특권 기능을 호출할 수 있는 신과 같은 계정이 있습니다. sudo 계정이 할 수 있는 기능으로 구성된 sudo 팔레트. 런타임에는 'sudo-key'라고 하는 하나의 sudo 계정만 있어야 합니다. 퍼블릭 블록체인 네트워크를 시작하기 전에 sudo 계정이 있으면 유용할 수 있지만 시작 후 대부분의 Parachain 프로젝트는 안전을 위해 이 Sudo 팔레트를 제거하는 경향이 있습니다.

이 팔레트가 어떻게 구현되는지 살펴보겠습니다.


소스 코드 분석



저장



이 팔레트에는 하나의 스토리지만 있습니다. 저장소 유형은 런타임에 대해 하나의 sudo 키 계정만 저장하는 StorageValue입니다.

#[pallet::storage]
#[pallet::getter(fn key)]
pub(super) type Key<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;


파견 호출



스도

Dispatch a call with Root origin.

Params

  • origin: who calls this function
  • call: Some Call type like dispatch calls. When building runtime, Compiler collects all the Call type into one single Call Enum type by #[pallet::call] macro. Code below is how Substrate deals with Call data type.



런타임에서

pub enum Call {

    System( <Pallet<Runtime> as Callable<Runtime>>::Call ),
    Balances( <Pallet<Runtime> as Callable<Runtime>>::Call ),
    Staking( <Pallet<Runtime> as Callable<Runtime>>::Call ),
    ...
}



예를 들어 Staking Pallet에서 모든 디스패치 호출은 Call enum 유형으로 정의됩니다.

pub enum Call {
   ...
   Staking(pallet_staking::Call::<Runtime>)
   ...
}

pub enum Call {
    bond { controller, value, payee },
    nominate { targets }
    ....
}


분석
  • 출처가 서명되었는지 확인하십시오.
  • 호출자가 'sudo 키 계정'인지 확인합니다
  • .
  • dispatch_bypass_filter: 디스패치 호출에서 원본 필터 확인을 건너뜁니다.
  • 'Sudid' 이벤트 발생
  • Sudo 계정이 수수료를 지불하지 않음

  • pub fn sudo(
        origin: OriginFor<T>,
        call: Box<<T as Config>::Call>,
    ) -> DispatchResultWithPostInfo {
    
        // 1
        let sender = ensure_signed(origin)?;
    
        // 2
        ensure!(Self::key().map_or(false, |k| sender == k), Error::<T>::RequireSudo);
    
        // 3 
        let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into());
    
        // 4
        Self::deposit_event(Event::Sudid { sudo_result: res.map(|_| ()).map_err(|e| e.error) });
    
        // 5 
        Ok(Pays::No.into())
    }
    


    sudo_unchecked_weight

    The logic of this method is same with _sudo but this time, Sudo account can control the Weight of the call. As we can see the parameter we give as _weight is used in the weight calculation in #[pallet::weight((*_weight, call.get_dispatch_info().class))] macro._

    Params

    • _weight: Amount of weight sudo want to control




    #[pallet::weight((*_weight, call.get_dispatch_info().class))]
    pub fn sudo_unchecked_weight(
        origin: OriginFor<T>,
        call: Box<<T as Config>::Call>,
        _weight: Weight,
    ) -> DispatchResultWithPostInfo {
    
        // Same logic with sudo
    }
    


    set_key

    Set sudo key with different account

    Params

    • new: New sudo key account. Type is Source type of StaticLookup trait. This type is for converting any source type into AccountId. Let's take a closer look!



    먼저 StaticLookup은 다음과 같이 정의됩니다.
  • 유형 소스: 보고자 하는 모든 유형
  • type Target: 변환하려는 유형
  • fn 조회: 소스 유형을 대상 유형으로 변환
  • fn unlookup: 대상 유형을 소스 유형으로 변환

  • pub trait StaticLookup {
        // 1
        pub type Source;
        // 2 
        pub type Target;
        // 3 
        fn lookup(s: Self::Source) -> Result<Self::Target, LookupError>
        // 4
        fn unlookup(t: Self::Target) -> Self::Source;
    }
    


    프레임 시스템 팔레트에서 Target은 AccountId 유형으로 정의되며 조회 메소드를 호출하면 찾고자 하는 모든 Source 유형을 AccountId 유형으로 변환할 수 있습니다.

    pub trait Config {
        ...
    
        type lookup: StaticLookup<Target = AccountId>
    
        ...
    }
    


    Runtime에서 frame_system의 config가 Runtime에 어떻게 구현되어 있는지 살펴보면 palette_indices가 lookup으로 정의되어 있습니다. 즉, palette_indices는 StaticLookup 트레이트를 구현해야 합니다.

    impl frame_system::Config for Runtime {
    
        ...
    
        type lookup = Indices(pallet_indices)
    
        ...
    }
    


    내부 palette_indices
  • Pallet용 정적 조회 구현
  • 다중 주소: 주소 형식의 유형
  • fn lookup_address(): 주소 형식에 따라 존재하는 경우 주소를 반환합니다. 여기서 찾고 있는 주소 형식의 유형은 Account Id 및 AccountIndex입니다.

  • // 1
    impl<T: Config> StaticLookup for Pallet<T> {
        type Source = MultiAddress<T::AccountId, T::AccountIndex>,
        type Target = T::AccountId,
    
        fn lookup(a: Self::Source) -> Result<Self::Target, LookupError> {
            Self::lookup_address(a).ok_or(LookupError);
        }
    
        fn unlookup(a: Self::Target) -> Self::Source {
           Multiaddress::Id(a)
        } 
    }
    
    // 2
    pub enum MultiAddress<AccountId, AccountIndex> {
        Id(AccountId), // public address
        Index(AccountIndex), // Account index for database
        ...
    }
    
    // 3
    impl<T: Config> Pallet<T> {
    
        fn lookup_index(index: T::AccountIndex) -> Option<T::AccountId> {
            // where Accounts is a storage that maps _AccountIndex_ to (AccountId, Balance)
            Accounts::<T>::get(index).map(|x| x.0)
    
        }  
    
        fn lookup_address(a: Multiaddress<T::AccountId, T::AccountIndex> -> Option<T::AccountId> {
             match a {
                  Multiaddress::Id(account) => Some(account)
                  Multiaddress::Index(index) => Self::lookup_index(index)
             }
        }
    }
    
    #[pallet::storage]
    pub type Accounts<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountIndex, (T::AccountId, BalanceOf<T>, bool)>;
    


    분석
  • 출처가 서명되었는지 확인하십시오.
  • 호출자가 'sudo 키 계정'인지 확인합니다
  • .
  • MultiAddress 유형을 AccountId로 변환합니다.
  • 'KeyChanged' 이벤트 발생
  • 키 저장소에 새 sudo 키 넣기
  • Sudo는 수수료를 지불하지 않습니다.

  • #[pallet::weight(0)]
    pub fn set_key(
        origin: OriginFor<T>,
        new: <T::Lookup as StaticLookup>::Source,
    ) -> DispatchResultWithPostInfo {
    
        // 1
        let sender = ensure_signed(origin)?;
    
        // 2
        ensure!(Self::key().map_or(false, |k| sender == k), Error::<T>::RequireSudo);
    
        // 3
        let new = T::Lookup::lookup(new)?;
    
        // 4
        Self::deposit_event(Event::KeyChanged { old_sudoer: Key::<T>::get() });
    
        // 5
        Key::<T>::put(&new);
    
        // 6
        Ok(Pays::No.into())
    }
    


    sudo_as

    Sudo account calls and dispatches a call with a signed account.
    Make signed account as sudo account which pays no fee
    Params

    • who: The one who calls the dispatch call




    pub fn sudo_as(
        origin: OriginFor<T>,
        who: <T::Lookup as StaticLookup>::Source,
        call: Box<<T as Config>::Call>,
    ) -> DispatchResultWithPostInfo {
    
        let sender = ensure_signed(origin)?;
        ensure!(Self::key().map_or(false, |k| sender == k), Error::<T>::RequireSudo);
    
        let who = T::Lookup::lookup(who)?;
    
        let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Signed(who).into());
    
        Self::deposit_event(Event::SudoAsDone {
                    sudo_result: res.map(|_| ()).map_err(|e| e.error),
                });
    
        Ok(Pays::No.into())
    }
    


    결론



    Sudo는 모든 것을 할 수 있는 Substrate 기반 체인에만 존재하는 특별한 기능이며 퍼블릭 블록체인에서는 선호되지 않습니다.

    질문이 있으시면 언제든지 의견을 남겨주세요.


    참조



    Sudo
    Frame System
    Indices

    좋은 웹페이지 즐겨찾기