【Substrate 第一行代码】 pallet 中使用自定义的 pallet

173 阅读2分钟

需求

之前我们在自定义的 pallet 中使用了现成的 balance pallet。接下来使用自己实现的另外一个 pallet。

定义 pallet-storage 实现储存功能,并实现读写方法。然后在 pallet-poe 中使用 pallet-storage。

pallet-storage 实现

在 pallets 中新建 storage 文件,文件结构和其他 pallet 结构相同。 在 src 文件下面新建 trait 文件,定义 pallet-storage 的抽象功能,类似接口。

trait.rs

pub trait StorageInterface{  
    type Value;  
    fn get_param() -> Self::Value;  
    fn set_param(v: Self::Value);
    }

实现 pallet_storage 功能 lib.rs


pub use pallet::*;
// 引入刚才定义的 StorageInterface
pub use traits::StorageInterface;

pub mod traits;

#[frame_support::pallet]
pub mod pallet {
  use codec::Codec;
  use frame_support::{
    pallet_prelude::*, 
    sp_runtime::traits::AtLeast32BitUnsigned, 
    sp_std::fmt::Debug,
  };
  use frame_system::pallet_prelude::*;

  #[pallet::pallet]
  #[pallet::generate_store(pub(super) trait Store)]
  pub struct Pallet<T>(_);

  #[pallet::config]
  pub trait Config: frame_system::Config {
    type Event: From<Event<Self>> 
      + IsType<<Self as frame_system::Config>::Event>;
    type Value: Member
      + Parameter
      + AtLeast32BitUnsigned
      + Codec
      + From<u32>
      + Into<u32>
      + Copy
      + Debug
      + Default
      + MaxEncodedLen
      + MaybeSerializeDeserialize;
  }

// 定义 StorageValue 作为最终的储存,在最后一部分 StorageInterface 实现中使用
  #[pallet::storage]
  pub type StorageInterfaceValue<T: Config> = 
       StorageValue<_, T::Value, ValueQuery>;

// event 不会用到
  #[pallet::event]
  #[pallet::generate_deposit(pub(super) fn deposit_event)]
  pub enum Event<T: Config> {
    FunctionCall,
  }
// call 也不会用到,随便写一个
  #[pallet::call]
  impl<T: Config> Pallet<T> {
    #[pallet::weight(0)]
    pub fn my_function(
      origin: OriginFor<T>,
    ) -> DispatchResultWithPostInfo {
      ensure_signed(origin)?;
      log::info!(target: "storage provider", "my function!");
      Self::deposit_event(Event::FunctionCall);

      Ok(().into())
    }
  }
}

// pallet实现了前面定义的 StorageInterface 这个 trait。 
// type Value 的用法就是之前讲的 trait 使用占位类型
impl<T: Config> StorageInterface for Pallet<T> {
  type Value = T::Value;

  fn get_param() -> Self::Value {
    StorageInterfaceValue::<T>::get()
  }

  fn set_param(v: Self::Value) {
    StorageInterfaceValue::<T>::put(v);
  }
}

为 pallet 实现 StorageInterface,可以参考【Substrate 第一行代码】 substrate 关键 rust 语法

pallet-poe 实现

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
  use codec::Codec;
  use frame_support::{
    pallet_prelude::*, 
    sp_runtime::traits::AtLeast32BitUnsigned, 
    sp_std::fmt::Debug,
  };
  use frame_system::pallet_prelude::*;
  // 引入 StorageInterface 
  use pallet_storage::traits::StorageInterface;

  #[pallet::pallet]
  #[pallet::generate_store(pub(super) trait Store)]
  pub struct Pallet<T>(_);

  // 3. Runtime Configuration Trait
  #[pallet::config]
  pub trait Config: frame_system::Config {
    type Event: From<Event<Self>> 
      + IsType<<Self as frame_system::Config>::Event>;
    type Value: Member
      + Parameter
      + AtLeast32BitUnsigned
      + Codec
      + From<u32>
      + Into<u32>
      + Copy
      + Debug
      + Default
      + MaxEncodedLen
      + MaybeSerializeDeserialize;
  
  //定义了MyStorage类型,要求 实现 StorageInterface
    type MyStorage: StorageInterface<Value = Self::Value>; 
  }

  // 5. Runtime Events
  // Can stringify event types to metadata.
  #[pallet::event]
  #[pallet::generate_deposit(pub(super) fn deposit_event)]
  pub enum Event<T: Config> {
    StoreEvent,
  }

  // 7. Extrinsics
  // Functions that are callable from outside the runtime.
  #[pallet::call]
  impl<T: Config> Pallet<T> {
    #[pallet::weight(0)]
    pub fn storage_value(
      origin: OriginFor<T>,
      value: T::Value, 
    ) -> DispatchResultWithPostInfo {
      ensure_signed(origin)?;
   //调用 StorageInterface中的函数
      T::MyStorage::set_param(value);
      let v = T::MyStorage::get_param();
      // log 打印
      log::info!(target: "poe", 
           "Value get from storage is: {:?}", v);

      Self::deposit_event(Event::StoreEvent);

      Ok(().into())
    }
  }
}

修改 runtime

runtime/Cargo.toml

[dependencies]
...
pallet-storage ={ 
      version = "4.0.0-dev", 
      default-features = false, 
      path = "../pallets/storage" }
pallet-poe = { 
      version = "4.0.0-dev", 
      default-features = false, 
      path = "../pallets/poe"}
 ...
 
[features]
default = ["std"]
std = [
  "codec/std",
  "scale-info/std",
  "frame-executive/std",
  "frame-support/std",
  "frame-system-rpc-runtime-api/std",
  "frame-system/std",
  "pallet-aura/std",
  "pallet-balances/std",
  "pallet-grandpa/std",
  "pallet-randomness-collective-flip/std",
  "pallet-sudo/std",
  "pallet-template/std",
  "pallet-simple-pallet/std",
  "pallet-use-storage/std",
  "pallet-use-errors/std",
  "pallet-ext-example/std",
  "pallet-use-hooks/std",
  "pallet-use-rpc/std",
  "pallet-use-config1/std",
  "pallet-use-config2/std",
  "pallet-storage/std",
  "pallet-poe/std"
  ]

runtime/src/lib.rs

实现两个 pallet


impl pallet_storage::Config for Runtime {
  type Event = Event;
  type Value = u32;
}

impl pallet_poe::Config for Runtime {
  type Event = Event;
  type Value = u32;
  type MyStorage = StorageProvider; 
}

construct_runtime 增加 pallet

construct_runtime!(
  pub enum Runtime where
    Block = Block,
    NodeBlock = opaque::Block,
    UncheckedExtrinsic = UncheckedExtrinsic
  {
    System: frame_system,
  ...
  //添加 Cargo 中配置的 pallet
    StorageProvider: pallet_storage_provider, 
    PoePallet: pallet_poe,
  }
);

总结

使用其他的 pallet 的过程其实就是 trait 的使用,要熟悉 trait 的关联类型,泛型参数的使用。

泛型与关联类型

【Substrate 第一行代码】 substrate 关键 rust 语法