Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
replace offchain worker example
Browse files Browse the repository at this point in the history
  • Loading branch information
gui1117 committed Sep 15, 2020
1 parent 44d3a8d commit 91eff0b
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 126 deletions.
260 changes: 135 additions & 125 deletions frame/example-offchain-worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,19 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_system::{
self as system,
ensure_signed,
ensure_none,
offchain::{
AppCrypto, CreateSignedTransaction, SendUnsignedTransaction, SendSignedTransaction,
SignedPayload, SigningTypes, Signer, SubmitTransaction,
}
};
use frame_support::{
debug,
dispatch::DispatchResult, decl_module, decl_storage, decl_event,
traits::Get,
};
use frame_support::{debug, storage::StorageValue, traits::Get};
use sp_core::crypto::KeyTypeId;
use sp_runtime::{
RuntimeDebug,
offchain::{http, Duration, storage::StorageValueRef},
traits::Zero,
transaction_validity::{
InvalidTransaction, ValidTransaction, TransactionValidity, TransactionSource,
TransactionPriority,
},
};
use codec::{Encode, Decode};
Expand Down Expand Up @@ -101,37 +93,6 @@ pub mod crypto {
}
}

/// This pallet's configuration trait
pub trait Trait: CreateSignedTransaction<Call<Self>> {
/// The identifier type for an offchain worker.
type AuthorityId: AppCrypto<Self::Public, Self::Signature>;

/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
/// The overarching dispatch call type.
type Call: From<Call<Self>>;

// Configuration parameters

/// A grace period after we send transaction.
///
/// To avoid sending too many transactions, we only attempt to send one
/// every `GRACE_PERIOD` blocks. We use Local Storage to coordinate
/// sending between distinct runs of this offchain worker.
type GracePeriod: Get<Self::BlockNumber>;

/// Number of blocks of cooldown after unsigned transaction is included.
///
/// This ensures that we only accept unsigned transactions once, every `UnsignedInterval` blocks.
type UnsignedInterval: Get<Self::BlockNumber>;

/// A configuration for base priority of unsigned transactions.
///
/// This is exposed so that it can be tuned for particular runtime, when
/// multiple pallets send unsigned transactions.
type UnsignedPriority: Get<TransactionPriority>;
}

/// Payload used by this example crate to hold price
/// data required to submit a transaction.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
Expand All @@ -147,35 +108,131 @@ impl<T: SigningTypes> SignedPayload<T> for PricePayload<T::Public, T::BlockNumbe
}
}

decl_storage! {
trait Store for Module<T: Trait> as ExampleOffchainWorker {
/// A vector of recently submitted prices.
pub use pallet::*;
#[frame_support::pallet(ExampleOffchainWorker)]
mod pallet {
use super::{CreateSignedTransaction, AppCrypto, PricePayload, TransactionType};
use frame_support::{pallet_prelude::*, debug};
use frame_system::pallet_prelude::*;

/// This pallet's configuration trait
#[pallet::trait_]
pub trait Trait: CreateSignedTransaction<Call<Self>> + frame_system::Trait {
/// The identifier type for an offchain worker.
type AuthorityId: AppCrypto<Self::Public, Self::Signature>;

/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Trait>::Event>;
/// The overarching dispatch call type.
type Call: From<Call<Self>>;

// Configuration parameters

/// A grace period after we send transaction.
///
/// This is used to calculate average price, should have bounded size.
Prices get(fn prices): Vec<u32>;
/// Defines the block when next unsigned transaction will be accepted.
/// To avoid sending too many transactions, we only attempt to send one
/// every `GRACE_PERIOD` blocks. We use Local Storage to coordinate
/// sending between distinct runs of this offchain worker.
type GracePeriod: Get<Self::BlockNumber>;

/// Number of blocks of cooldown after unsigned transaction is included.
///
/// To prevent spam of unsigned (and unpayed!) transactions on the network,
/// we only allow one transaction every `T::UnsignedInterval` blocks.
/// This storage entry defines when new transaction is going to be accepted.
NextUnsignedAt get(fn next_unsigned_at): T::BlockNumber;
/// This ensures that we only accept unsigned transactions once, every `UnsignedInterval`
/// blocks.
type UnsignedInterval: Get<Self::BlockNumber>;

/// A configuration for base priority of unsigned transactions.
///
/// This is exposed so that it can be tuned for particular runtime, when
/// multiple pallets send unsigned transactions.
type UnsignedPriority: Get<TransactionPriority>;
}
}

decl_event!(
/// A vector of recently submitted prices.
///
/// This is used to calculate average price, should have bounded size.
#[pallet::storage]
#[pallet::generate_getter(fn prices)]
pub(crate) type Prices = StorageValueType<_, Vec<u32>, ValueQuery>;

/// Defines the block when next unsigned transaction will be accepted.
///
/// To prevent spam of unsigned (and unpayed!) transactions on the network,
/// we only allow one transaction every `T::UnsignedInterval` blocks.
/// This storage entry defines when new transaction is going to be accepted.
#[pallet::storage]
#[pallet::generate_getter(fn next_unsigned_at)]
pub(crate) type NextUnsignedAt<T: Trait> = StorageValueType<_, T::BlockNumber, ValueQuery>;

/// Events generated by the module.
pub enum Event<T> where AccountId = <T as frame_system::Trait>::AccountId {
/// Event generated when new price is accepted to contribute to the average.
#[pallet::event]
#[pallet::metadata(<T as frame_system::Trait>::AccountId = AccountId)]
pub enum Event<T: Trait> {
/// Event generated when new price is accepted to contribute to the average.
/// [price, who]
NewPrice(u32, AccountId),
NewPrice(u32, <T as frame_system::Trait>::AccountId),
}
);

decl_module! {
/// A public part of the pallet.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default;
#[pallet::module]
#[pallet::generate(fn deposit_event)]
pub struct Module<T>(PhantomData<T>);

#[pallet::module_interface]
impl<T: Trait> ModuleInterface<BlockNumberFor<T>> for Module<T> {
/// Offchain Worker entry point.
///
/// By implementing `fn offchain_worker` within `decl_module!` you declare a new offchain
/// worker.
/// This function will be called when the node is fully synced and a new best block is
/// succesfuly imported.
/// Note that it's not guaranteed for offchain workers to run on EVERY block, there might
/// be cases where some blocks are skipped, or for some the worker runs twice (re-orgs),
/// so the code should be able to handle that.
/// You can use `Local Storage` API to coordinate runs of the worker.
fn offchain_worker(block_number: T::BlockNumber) {
// It's a good idea to add logs to your offchain workers.
// Using the `frame_support::debug` module you have access to the same API exposed by
// the `log` crate.
// Note that having logs compiled to WASM may cause the size of the blob to increase
// significantly. You can use `RuntimeDebug` custom derive to hide details of the types
// in WASM or use `debug::native` namespace to produce logs only when the worker is
// running natively.
debug::native::info!("Hello World from offchain workers!");

// Since off-chain workers are just part of the runtime code, they have direct access
// to the storage and other included pallets.
//
// We can easily import `frame_system` and retrieve a block hash of the parent block.
let parent_hash = <frame_system::Module<T>>::block_hash(block_number - 1.into());
debug::debug!("Current block: {:?} (parent hash: {:?})", block_number, parent_hash);

// It's a good practice to keep `fn offchain_worker()` function minimal, and move most
// of the code to separate `impl` block.
// Here we call a helper function to calculate current average price.
// This function reads storage entries of the current state.
let average: Option<u32> = Self::average_price();
debug::debug!("Current price: {:?}", average);

// For this example we are going to send both signed and unsigned transactions
// depending on the block number.
// Usually it's enough to choose one or the other.
let should_send = Self::choose_transaction_type(block_number);
let res = match should_send {
TransactionType::Signed => Self::fetch_price_and_send_signed(),
TransactionType::UnsignedForAny => Self::fetch_price_and_send_unsigned_for_any_account(block_number),
TransactionType::UnsignedForAll => Self::fetch_price_and_send_unsigned_for_all_accounts(block_number),
TransactionType::Raw => Self::fetch_price_and_send_raw_unsigned(block_number),
TransactionType::None => Ok(()),
};
if let Err(e) = res {
debug::error!("Error: {}", e);
}
}
}

/// A public part of the pallet.
#[pallet::call]
impl<T: Trait> Call for Module<T> {
/// Submit new price to the list.
///
/// This method is a public function of the module and can be called from within
Expand All @@ -190,13 +247,13 @@ decl_module! {
/// working and receives (and provides) meaningful data.
/// This example is not focused on correctness of the oracle itself, but rather its
/// purpose is to showcase offchain worker capabilities.
#[weight = 0]
pub fn submit_price(origin, price: u32) -> DispatchResult {
#[pallet::weight(0)]
pub fn submit_price(origin: OriginFor<T>, price: u32) -> DispatchResultWithPostInfo {
// Retrieve sender of the transaction.
let who = ensure_signed(origin)?;
// Add the price to the on-chain list.
Self::add_price(who, price);
Ok(())
Ok(().into())
}

/// Submit new price to the list via unsigned transaction.
Expand All @@ -215,84 +272,37 @@ decl_module! {
///
/// This example is not focused on correctness of the oracle itself, but rather its
/// purpose is to showcase offchain worker capabilities.
#[weight = 0]
pub fn submit_price_unsigned(origin, _block_number: T::BlockNumber, price: u32)
-> DispatchResult
#[pallet::weight(0)]
pub fn submit_price_unsigned(
origin: OriginFor<T>,
_block_number: T::BlockNumber,
price: u32
) -> DispatchResultWithPostInfo
{
// This ensures that the function can only be called via unsigned transaction.
ensure_none(origin)?;
// Add the price to the on-chain list, but mark it as coming from an empty address.
Self::add_price(Default::default(), price);
// now increment the block number at which we expect next unsigned transaction.
let current_block = <system::Module<T>>::block_number();
let current_block = <frame_system::Module<T>>::block_number();
<NextUnsignedAt<T>>::put(current_block + T::UnsignedInterval::get());
Ok(())
Ok(().into())
}

#[weight = 0]
#[pallet::weight(0)]
pub fn submit_price_unsigned_with_signed_payload(
origin,
origin: OriginFor<T>,
price_payload: PricePayload<T::Public, T::BlockNumber>,
_signature: T::Signature,
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
// This ensures that the function can only be called via unsigned transaction.
ensure_none(origin)?;
// Add the price to the on-chain list, but mark it as coming from an empty address.
Self::add_price(Default::default(), price_payload.price);
// now increment the block number at which we expect next unsigned transaction.
let current_block = <system::Module<T>>::block_number();
let current_block = <frame_system::Module<T>>::block_number();
<NextUnsignedAt<T>>::put(current_block + T::UnsignedInterval::get());
Ok(())
}

/// Offchain Worker entry point.
///
/// By implementing `fn offchain_worker` within `decl_module!` you declare a new offchain
/// worker.
/// This function will be called when the node is fully synced and a new best block is
/// succesfuly imported.
/// Note that it's not guaranteed for offchain workers to run on EVERY block, there might
/// be cases where some blocks are skipped, or for some the worker runs twice (re-orgs),
/// so the code should be able to handle that.
/// You can use `Local Storage` API to coordinate runs of the worker.
fn offchain_worker(block_number: T::BlockNumber) {
// It's a good idea to add logs to your offchain workers.
// Using the `frame_support::debug` module you have access to the same API exposed by
// the `log` crate.
// Note that having logs compiled to WASM may cause the size of the blob to increase
// significantly. You can use `RuntimeDebug` custom derive to hide details of the types
// in WASM or use `debug::native` namespace to produce logs only when the worker is
// running natively.
debug::native::info!("Hello World from offchain workers!");

// Since off-chain workers are just part of the runtime code, they have direct access
// to the storage and other included pallets.
//
// We can easily import `frame_system` and retrieve a block hash of the parent block.
let parent_hash = <system::Module<T>>::block_hash(block_number - 1.into());
debug::debug!("Current block: {:?} (parent hash: {:?})", block_number, parent_hash);

// It's a good practice to keep `fn offchain_worker()` function minimal, and move most
// of the code to separate `impl` block.
// Here we call a helper function to calculate current average price.
// This function reads storage entries of the current state.
let average: Option<u32> = Self::average_price();
debug::debug!("Current price: {:?}", average);

// For this example we are going to send both signed and unsigned transactions
// depending on the block number.
// Usually it's enough to choose one or the other.
let should_send = Self::choose_transaction_type(block_number);
let res = match should_send {
TransactionType::Signed => Self::fetch_price_and_send_signed(),
TransactionType::UnsignedForAny => Self::fetch_price_and_send_unsigned_for_any_account(block_number),
TransactionType::UnsignedForAll => Self::fetch_price_and_send_unsigned_for_all_accounts(block_number),
TransactionType::Raw => Self::fetch_price_and_send_raw_unsigned(block_number),
TransactionType::None => Ok(()),
};
if let Err(e) = res {
debug::error!("Error: {}", e);
}
Ok(().into())
}
}
}
Expand Down Expand Up @@ -631,7 +641,7 @@ impl<T: Trait> Module<T> {
.expect("The average is not empty, because it was just mutated; qed");
debug::info!("Current average price is: {}", average);
// here we are raising the NewPrice event
Self::deposit_event(RawEvent::NewPrice(price, who));
Self::deposit_event(Event::NewPrice(price, who));
}

/// Calculate current average price.
Expand All @@ -654,7 +664,7 @@ impl<T: Trait> Module<T> {
return InvalidTransaction::Stale.into();
}
// Let's make sure to reject transactions from the future.
let current_block = <system::Module<T>>::block_number();
let current_block = <frame_system::Module<T>>::block_number();
if &current_block < block_number {
return InvalidTransaction::Future.into();
}
Expand Down
3 changes: 2 additions & 1 deletion frame/support/procedural/src/pallet/parse/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ impl StorageDef {

if query_kind.is_none() && getter.is_some() {
let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \
identifiable. QueryKind must be `OptionQuery` or `ValueQuery` to be identifiable.";
identifiable. QueryKind must be `OptionQuery`, `ValueQuery`, or default one to be \
identifiable.";
return Err(syn::Error::new(getter.unwrap().span(), msg));
}

Expand Down
3 changes: 3 additions & 0 deletions frame/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,9 @@ impl<T: Trait> Lookup for ChainContext<T> {
}

pub mod pallet_prelude {
pub use crate::ensure_signed;
pub use crate::ensure_none;
pub use crate::ensure_root;
pub type OriginFor<T> = <T as crate::Trait>::Origin;
pub type BlockNumberFor<T> = <T as crate::Trait>::BlockNumber;
}

0 comments on commit 91eff0b

Please sign in to comment.