// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: MIT-0 // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //! # Basic Example Pezpallet //! //! A pezpallet demonstrating concepts, APIs and structures common to most FRAME runtimes. //! //! **This pezpallet serves as an example and is not meant to be used in production.** //! //! > Made with *Bizinikiwi*, for *Pezkuwi*. //! //! [![github]](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/bizinikiwi/pezframe/examples/basic) //! [![pezkuwi]](https://pezkuwichain.io) //! //! [pezkuwi]: https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! //! ## Pezpallet API //! //! See the [`pezpallet`] module for more information about the interfaces this pezpallet exposes, //! including its configuration trait, dispatchables, storage items, events and errors. //! //! ## Overview //! //! This pezpallet provides basic examples of using: //! //! - A custom weight calculator able to classify a call's dispatch class (see: //! [`pezframe_support::dispatch::DispatchClass`]) //! - Pezpallet hooks to implement some custom logic that's executed before and after a block is //! imported (see: [`pezframe_support::traits::Hooks`]) //! - Inherited weight annotation for pezpallet calls, used to create less repetition for calls that //! use the [`Config::WeightInfo`] trait to calculate call weights. This can also be overridden, //! as demonstrated by [`Call::set_dummy`]. //! - A private function that performs a storage update. //! - A simple transaction extension implementation (see: //! [`pezsp_runtime::traits::TransactionExtension`]) which increases the priority of the //! [`Call::set_dummy`] if it's present and drops any transaction with an encoded length higher //! than 200 bytes. // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use alloc::vec::Vec; use codec::{Decode, DecodeWithMemTracking, Encode}; use core::marker::PhantomData; use log::info; use pezframe_support::{ dispatch::{ClassifyDispatch, DispatchClass, DispatchResult, Pays, PaysFee, WeighData}, pezpallet_prelude::TransactionSource, traits::IsSubType, weights::Weight, }; use pezframe_system::ensure_signed; use pezsp_runtime::{ impl_tx_ext_default, traits::{ Bounded, DispatchInfoOf, DispatchOriginOf, SaturatedConversion, Saturating, TransactionExtension, ValidateResult, }, transaction_validity::{InvalidTransaction, ValidTransaction}, }; use scale_info::TypeInfo; // Re-export pezpallet items so that they can be accessed from the crate namespace. pub use pezpallet::*; #[cfg(test)] mod tests; mod benchmarking; pub mod weights; pub use weights::*; /// A type alias for the balance type from this pezpallet's point of view. type BalanceOf = ::Balance; const MILLICENTS: u32 = 1_000_000_000; // A custom weight calculator tailored for the dispatch call `set_dummy()`. This actually examines // the arguments and makes a decision based upon them. // // The `WeightData` trait has access to the arguments of the dispatch that it wants to assign a // weight to. Nonetheless, the trait itself cannot make any assumptions about what the generic type // of the arguments (`T`) is. Based on our needs, we could replace `T` with a more concrete type // while implementing the trait. The `pezpallet::weight` expects whatever implements `WeighData` // to replace `T` with a tuple of the dispatch arguments. This is exactly how we will craft the // implementation below. // // The rules of `WeightForSetDummy` are as follows: // - The final weight of each dispatch is calculated as the argument of the call multiplied by the // parameter given to the `WeightForSetDummy`'s constructor. // - assigns a dispatch class `operational` if the argument of the call is more than 1000. // // More information can be read at: // - https://docs.pezkuwichain.io/main-docs/build/tx-weights-fees/ // // Manually configuring weight is an advanced operation and what you really need may well be // fulfilled by running the benchmarking toolchain. Refer to `benchmarking.rs` file. struct WeightForSetDummy(BalanceOf); impl WeighData<(&BalanceOf,)> for WeightForSetDummy { fn weigh_data(&self, target: (&BalanceOf,)) -> Weight { let multiplier = self.0; // *target.0 is the amount passed into the extrinsic let cents = *target.0 / >::from(MILLICENTS); Weight::from_parts((cents * multiplier).saturated_into::(), 0) } } impl ClassifyDispatch<(&BalanceOf,)> for WeightForSetDummy { fn classify_dispatch(&self, target: (&BalanceOf,)) -> DispatchClass { if *target.0 > >::from(1000u32) { DispatchClass::Operational } else { DispatchClass::Normal } } } impl PaysFee<(&BalanceOf,)> for WeightForSetDummy { fn pays_fee(&self, _target: (&BalanceOf,)) -> Pays { Pays::Yes } } // Definition of the pezpallet logic, to be aggregated at runtime definition through // `construct_runtime`. #[pezframe_support::pezpallet] pub mod pezpallet { // Import various types used to declare pezpallet in scope. use super::*; use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; /// Our pezpallet's configuration trait. All our types and constants go in here. If the /// pezpallet is dependent on specific other pallets, then their configuration traits /// should be added to our implied traits list. /// /// `pezframe_system::Config` should always be included. #[pezpallet::config] pub trait Config: pezpallet_balances::Config + pezframe_system::Config { // Setting a constant config parameter from the runtime #[pezpallet::constant] type MagicNumber: Get; /// Type representing the weight of this pezpallet type WeightInfo: WeightInfo; } // Simple declaration of the `Pezpallet` type. It is placeholder we use to implement traits and // method. #[pezpallet::pezpallet] pub struct Pezpallet(_); // This pezpallet implements the [`pezframe_support::traits::Hooks`] trait to define some logic // to execute in some context. #[pezpallet::hooks] impl Hooks> for Pezpallet { // `on_initialize` is executed at the beginning of the block before any extrinsic are // dispatched. // // This function must return the weight consumed by `on_initialize` and `on_finalize`. fn on_initialize(_n: BlockNumberFor) -> Weight { // Anything that needs to be done at the start of the block. // We don't do anything here. Weight::zero() } // `on_finalize` is executed at the end of block after all extrinsic are dispatched. fn on_finalize(_n: BlockNumberFor) { // Perform necessary data/state clean up here. } // A runtime code run after every block and have access to extended set of APIs. // // For instance you can generate extrinsics for the upcoming produced block. fn offchain_worker(_n: BlockNumberFor) { // We don't do anything here. // but we could dispatch extrinsic (transaction/unsigned/inherent) using // pezsp_io::submit_extrinsic. // To see example on offchain worker, please refer to example-offchain-worker pezpallet // accompanied in this repository. } } // The call declaration. This states the entry points that we handle. The // macro takes care of the marshalling of arguments and dispatch. // // Anyone can have these functions execute by signing and submitting // an extrinsic. Ensure that calls into each of these execute in a time, memory and // using storage space proportional to any costs paid for by the caller or otherwise the // difficulty of forcing the call to happen. // // Generally you'll want to split these into three groups: // - Public calls that are signed by an external account. // - Root calls that are allowed to be made only by the governance system. // - Unsigned calls that can be of two kinds: // * "Inherent extrinsics" that are opinions generally held by the block authors that build // child blocks. // * Unsigned Transactions that are of intrinsic recognizable utility to the network, and are // validated by the runtime. // // Information about where this dispatch initiated from is provided as the first argument // "origin". As such functions must always look like: // // `fn foo(origin: OriginFor, bar: Bar, baz: Baz) -> DispatchResultWithPostInfo { ... }` // // The `DispatchResultWithPostInfo` is required as part of the syntax (and can be found at // `pezpallet_prelude::DispatchResultWithPostInfo`). // // There are three entries in the `pezframe_system::Origin` enum that correspond // to the above bullets: `::Signed(AccountId)`, `::Root` and `::None`. You should always match // against them as the first thing you do in your function. There are three convenience calls // in system that do the matching for you and return a convenient result: `ensure_signed`, // `ensure_root` and `ensure_none`. #[pezpallet::call(weight(::WeightInfo))] impl Pezpallet { /// This is your public interface. Be extremely careful. /// This is just a simple example of how to interact with the pezpallet from the external /// world. // This just increases the value of `Dummy` by `increase_by`. // // Since this is a dispatched function there are two extremely important things to // remember: // // - MUST NOT PANIC: Under no circumstances (save, perhaps, storage getting into an // irreparably damaged state) must this function panic. // - NO SIDE-EFFECTS ON ERROR: This function must either complete totally (and return // `Ok(())` or it must have no side-effects on storage and return `Err('Some reason')`. // // The first is relatively easy to audit for - just ensure all panickers are removed from // logic that executes in production (which you do anyway, right?!). To ensure the second // is followed, you should do all tests for validity at the top of your function. This // is stuff like checking the sender (`origin`) or that state is such that the operation // makes sense. // // Once you've determined that it's all good, then enact the operation and change storage. // If you can't be certain that the operation will succeed without substantial computation // then you have a classic blockchain attack scenario. The normal way of managing this is // to attach a bond to the operation. As the first major alteration of storage, reserve // some value from the sender's account (`Balances` Pezpallet has a `reserve` function for // exactly this scenario). This amount should be enough to cover any costs of the // substantial execution in case it turns out that you can't proceed with the operation. // // If it eventually transpires that the operation is fine and, therefore, that the // expense of the checks should be borne by the network, then you can refund the reserved // deposit. If, however, the operation turns out to be invalid and the computation is // wasted, then you can burn it or repatriate elsewhere. // // Security bonds ensure that attackers can't game it by ensuring that anyone interacting // with the system either progresses it or pays for the trouble of faffing around with // no progress. // // If you don't respect these rules, it is likely that your chain will be attackable. // // Each transaction must define a `#[pezpallet::weight(..)]` attribute to convey a set of // static information about its dispatch. FRAME System and FRAME Executive pezpallet then // use this information to properly execute the transaction, whilst keeping the total load // of the chain in a moderate rate. // // The parenthesized value of the `#[pezpallet::weight(..)]` attribute can be any type that // implements a set of traits, namely [`WeighData`], [`ClassifyDispatch`], and // [`PaysFee`]. The first conveys the weight (a numeric representation of pure // execution time and difficulty) of the transaction and the second demonstrates the // [`DispatchClass`] of the call, the third gives whereas extrinsic must pay fees or not. // A higher weight means a larger transaction (less of which can be placed in a single // block). // // The weight for this extrinsic we rely on the auto-generated `WeightInfo` from the // benchmark toolchain. #[pezpallet::call_index(0)] pub fn accumulate_dummy(origin: OriginFor, increase_by: T::Balance) -> DispatchResult { // This is a public call, so we ensure that the origin is some signed account. let _sender = ensure_signed(origin)?; // Read the value of dummy from storage. // let dummy = Dummy::::get(); // Calculate the new value. // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); // Put the new value into storage. // >::put(new_dummy); // Will also work with a reference: // >::put(&new_dummy); // Here's the new one of read and then modify the value. >::mutate(|dummy| { // Using `saturating_add` instead of a regular `+` to avoid overflowing let new_dummy = dummy.map_or(increase_by, |d| d.saturating_add(increase_by)); *dummy = Some(new_dummy); }); // Let's deposit an event to let the outside world know this happened. Self::deposit_event(Event::AccumulateDummy { balance: increase_by }); // All good, no refund. Ok(()) } /// A privileged call; in this case it resets our dummy value to something new. // Implementation of a privileged call. The `origin` parameter is ROOT because // it's not (directly) from an extrinsic, but rather the system as a whole has decided // to execute it. Different runtimes have different reasons for allow privileged // calls to be executed - we don't need to care why. Because it's privileged, we can // assume it's a one-off operation and substantial processing/storage/memory can be used // without worrying about gameability or attack scenarios. // // The weight for this extrinsic we use our own weight object `WeightForSetDummy` to // determine its weight #[pezpallet::call_index(1)] #[pezpallet::weight(WeightForSetDummy::(>::from(100u32)))] pub fn set_dummy( origin: OriginFor, #[pezpallet::compact] new_value: T::Balance, ) -> DispatchResult { ensure_root(origin)?; // Print out log or debug message in the console via log::{error, warn, info, debug, // trace}, accepting format strings similar to `println!`. // https://docs.pezkuwichain.io/bizinikiwi/master/pezsp_io/logging/fn.log.html // https://docs.pezkuwichain.io/bizinikiwi/master/pezframe_support/constant.LOG_TARGET.html info!("New value is now: {:?}", new_value); // Put the new value into storage. >::put(new_value); Self::deposit_event(Event::SetDummy { balance: new_value }); // All good, no refund. Ok(()) } } /// Events are a simple means of reporting specific conditions and /// circumstances that have happened that users, Dapps and/or chain explorers would find /// interesting and otherwise difficult to detect. #[pezpallet::event] /// This attribute generate the function `deposit_event` to deposit one of this pezpallet event, /// it is optional, it is also possible to provide a custom implementation. #[pezpallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // Just a normal `enum`, here's a dummy event to ensure it compiles. /// Dummy event, just here so there's a generic type that's used. AccumulateDummy { balance: BalanceOf, }, SetDummy { balance: BalanceOf, }, SetBar { account: T::AccountId, balance: BalanceOf, }, } // pezpallet::storage attributes allow for type-safe usage of the Bizinikiwi storage database, // so you can keep things around between blocks. // // Any storage must be one of `StorageValue`, `StorageMap` or `StorageDoubleMap`. // The first generic holds the prefix to use and is generated by the macro. // The query kind is either `OptionQuery` (the default) or `ValueQuery`. // - for `type Foo = StorageValue<_, u32, OptionQuery>`: // - `Foo::put(1); Foo::get()` returns `Some(1)`; // - `Foo::kill(); Foo::get()` returns `None`. // - for `type Foo = StorageValue<_, u32, ValueQuery>`: // - `Foo::put(1); Foo::get()` returns `1`; // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). #[pezpallet::storage] pub(super) type Dummy = StorageValue<_, T::Balance>; // A map that has enumerable entries. #[pezpallet::storage] pub(super) type Bar = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance>; // this one uses the query kind: `ValueQuery`, we'll demonstrate the usage of 'mutate' API. #[pezpallet::storage] pub(super) type Foo = StorageValue<_, T::Balance, ValueQuery>; #[pezpallet::storage] pub type CountedMap = CountedStorageMap<_, Blake2_128Concat, u8, u16>; // The genesis config type. #[pezpallet::genesis_config] #[derive(pezframe_support::DefaultNoBound)] pub struct GenesisConfig { pub dummy: T::Balance, pub bar: Vec<(T::AccountId, T::Balance)>, pub foo: T::Balance, } // The build of genesis for the pezpallet. #[pezpallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { >::put(&self.dummy); for (a, b) in &self.bar { >::insert(a, b); } >::put(&self.foo); } } } // The main implementation block for the pezpallet. Functions here fall into three broad // categories: // - Public interface. These are functions that are `pub` and generally fall into inspector // functions that do not write to storage and operation functions that do. // - Private functions. These are your usual private utilities unavailable to other pallets. impl Pezpallet { // Add public immutables and private mutables. #[allow(dead_code)] fn accumulate_foo(origin: T::RuntimeOrigin, increase_by: T::Balance) -> DispatchResult { let _sender = ensure_signed(origin)?; let prev = Foo::::get(); // Because Foo has 'default', the type of 'foo' in closure is the raw type instead of an // Option<> type. let result = Foo::::mutate(|foo| { *foo = foo.saturating_add(increase_by); *foo }); assert!(prev + increase_by == result); Ok(()) } } // Similar to other FRAME pallets, your pezpallet can also define a transaction extension and // perform some checks and [pre/post]processing [before/after] the transaction. A transaction // extension can be any decodable type that implements `TransactionExtension`. See the trait // definition for the full list of bounds. As a convention, you can follow this approach to create // an extension for your pezpallet: // - If the extension does not carry any data, then use a tuple struct with just a `marker` // (needed for the compiler to accept `T: Config`) will suffice. // - Otherwise, create a tuple struct which contains the external data. Of course, for the entire // struct to be decodable, each individual item also needs to be decodable. // // Note that a transaction extension can also indicate that a particular data must be present in the // _signing payload_ of a transaction by providing an implementation for the `implicit` method. This // example will not cover this type of extension. See `CheckSpecVersion` in [FRAME // System](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/bizinikiwi/pezframe/system#signed-extensions) // for an example. // // Using the extension, you can add some hooks to the life cycle of each transaction. Note that by // default, an extension is applied to all `Call` functions (i.e. all transactions). the `Call` enum // variant is given to each function of `TransactionExtension`. Hence, you can filter based on // pezpallet or a particular call if needed. // // Some extra information, such as encoded length, some static dispatch info like weight and the // sender of the transaction (if signed) are also provided. // // The full list of hooks that can be added to a transaction extension can be found in the // `TransactionExtension` trait definition. // // The transaction extensions are aggregated in the runtime file of a bizinikiwi chain. All // extensions should be aggregated in a tuple and passed to the `CheckedExtrinsic` and // `UncheckedExtrinsic` types defined in the runtime. Lookup `pub type TxExtension = (...)` in // `node/runtime` and `node-template` for an example of this. /// A simple transaction extension that checks for the `set_dummy` call. In that case, it increases /// the priority and prints some log. /// /// Additionally, it drops any transaction with an encoded length higher than 200 bytes. No /// particular reason why, just to demonstrate the power of transaction extensions. #[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct WatchDummy(PhantomData); impl core::fmt::Debug for WatchDummy { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "WatchDummy") } } impl TransactionExtension<::RuntimeCall> for WatchDummy where ::RuntimeCall: IsSubType>, { const IDENTIFIER: &'static str = "WatchDummy"; type Implicit = (); type Pre = (); type Val = (); fn validate( &self, origin: DispatchOriginOf<::RuntimeCall>, call: &::RuntimeCall, _info: &DispatchInfoOf<::RuntimeCall>, len: usize, _self_implicit: Self::Implicit, _inherited_implication: &impl Encode, _source: TransactionSource, ) -> ValidateResult::RuntimeCall> { // if the transaction is too big, just drop it. if len > 200 { return Err(InvalidTransaction::ExhaustsResources.into()); } // check for `set_dummy` let validity = match call.is_sub_type() { Some(Call::set_dummy { .. }) => { pezsp_runtime::print("set_dummy was received."); let valid_tx = ValidTransaction { priority: Bounded::max_value(), ..Default::default() }; valid_tx }, _ => Default::default(), }; Ok((validity, (), origin)) } impl_tx_ext_default!(::RuntimeCall; weight prepare); }