Offchain Worker (below)

Case 7: Initiate a signed transaction to the chain

Purpose

  1. Modify Node, runtime, pallet files

  2. mostly boilerplate code

  3. The signature transaction requires an account, and the execution of the transaction will charge tx fee from this account

The content of this section focuses on configuration, now we will set some necessary configuration items

1.substrate-node-template/node/src/service.rs

--let client = Arc::new(client);+++// Generate an account and put it into the keystore, so that the pallet can obtain the account, offchain uses if config.offchain_worker.enabled { let keystore = keystore_container.sync_keystore() ; sp_keystore::SyncCryptoStore::sr25519_generate_new( &*keystore, node_template_runtime::pallet_template::KEY_TYPE, Some("//Alice"), //Alice account is added, for testing, it has a lot of balance, and it is also Sudo) .expect("Creating key with account Alice should succeed."); }

ubstrate-node-template/node/Cargo.toml

sp-keystore = {version ="0.12.0",git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" }

2.substrate-node-template/runtime/src/lib.rs

Note: branch = "polkadot-v0.9.30" version Call has changed to RuntimeCall

use codec::Encode;use sp_runtime::SaturatedConversion; //Introduce two traitimpl pallet_sudo::Config for Runtime {type RuntimeEvent = RuntimeEvent;type RuntimeCall = RuntimeCall;}// When the offchain worker calls the function in the pallet , where the real transaction is generated impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for RuntimewhereRuntimeCall: From<LocalCall>,{fn create_transaction<C: frame_system::offchain::AppCrypto<Self::Public, Self ::Signature>>(call: RuntimeCall,public: <Signature as sp_runtime::traits::Verify>::Signer,account: AccountId,nonce: Index,) -> Option<(RuntimeCall,<UncheckedExtrinsic as sp_runtime::traits ::Extrinsic>::SignaturePayload,)> {let tip = 0;let ​​period =BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;let current_block = System::block_number().saturated_into::<u64>().saturating_sub(1);let era = generic::Era::mortal(period, current_block);let extra = (frame_system::CheckNonZeroSender::<Runtime>::new(),frame_system::CheckSpecVersion::<Runtime>::new(),frame_system::CheckTxVersion::<Runtime>::new(),frame_system::CheckGenesis::<Runtime>::new(),frame_system::CheckEra::<Runtime>::from(era),frame_system::CheckNonce::<Runtime>::from(nonce),frame_system::CheckWeight::<Runtime>::new(),//pallet_asset_tx_payment::ChargeAssetTxPayment::<Runtime>::from(tip, None),pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),);let raw_payload = SignedPayload::new(call, extra).map_err(|_| {//log::warn!("Unable to create signed payload: {:?}", e);}).ok()?;let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?;let address = account;let (call, extra, _) = raw_payload.deconstruct();Some((call, (sp_runtime::MultiAddress::Id(address), signature.into(), extra)))}}impl frame_system::offchain::SigningTypes for Runtime {type Public = <Signature as sp_runtime::traits::Verify>::Signer;type Signature = Signature;}impl<C> frame_system::offchain::SendTransactionTypes<C> for RuntimewhereRuntimeCall: From<C>,{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}/// Configure the pallet-template in pallets/template./// Configure the pallet-template in pallets/template.impl pallet_template::Config for Runtime {type RuntimeEvent = RuntimeEvent;type AuthorityId = pallet_template::crypto::OcwAuthId;}extra)))}}impl frame_system::offchain::SigningTypes for Runtime {type Public = <Signature as sp_runtime::traits::Verify>::Signer;type Signature = Signature;}impl<C> frame_system::offchain::SendTransactionTypes<C> for RuntimewhereRuntimeCall: From<C>,{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}/// Configure the pallet-template in pallets/template./// Configure the pallet-template in pallets/template.impl pallet_template::Config for Runtime {type RuntimeEvent = RuntimeEvent;type AuthorityId = pallet_template::crypto::OcwAuthId;}extra)))}}impl frame_system::offchain::SigningTypes for Runtime {type Public = <Signature as sp_runtime::traits::Verify>::Signer;type Signature = Signature;}impl<C> frame_system::offchain::SendTransactionTypes<C> for RuntimewhereRuntimeCall: From<C>,{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}/// Configure the pallet-template in pallets/template./// Configure the pallet-template in pallets/template.impl pallet_template::Config for Runtime {type RuntimeEvent = RuntimeEvent;type AuthorityId = pallet_template::crypto::OcwAuthId;}{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}/// Configure the pallet-template in pallets/template./// Configure the pallet-template in pallets/template.impl pallet_template::Config for Runtime {type RuntimeEvent = RuntimeEvent;type AuthorityId = pallet_template::crypto::OcwAuthId;}{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}/// Configure the pallet-template in pallets/template./// Configure the pallet-template in pallets/template.impl pallet_template::Config for Runtime {type RuntimeEvent = RuntimeEvent;type AuthorityId = pallet_template::crypto::OcwAuthId;}

3.substrate-node-template/pallets/template/src/lib.rs

use frame_system::{   offchain::{       AppCrypto, CreateSignedTransaction, SendSignedTransaction,       Signer,   },};use sp_core::crypto::KeyTypeId;pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"ocwd");pub mod crypto {   use super::KEY_TYPE;   use sp_core::sr25519::Signature as Sr25519Signature;   use sp_runtime::{       app_crypto::{app_crypto, sr25519},       traits::Verify,       MultiSignature, MultiSigner,   };   app_crypto!(sr25519, KEY_TYPE);   pub struct OcwAuthId;   impl frame_system::offchain::AppCrypto<MultiSigner, MultiSignature> for OcwAuthId {       type RuntimeAppPublic = Public;       type GenericSignature = sp_core::sr25519::Signature;       type GenericPublic = sp_core::sr25519::Public;   }   impl frame_system::offchain::AppCrypto<<Sr25519Signature as Verify>::Signer, Sr25519Signature>for OcwAuthId { type RuntimeAppPublic = Public; type GenericSignature = sp_core::sr25519::Signature; type GenericPublic = sp_core::sr25519::Public; }}pub mod pallet {use super::*;use frame_support::pallet_prelude::* ;use frame_system::pallet_prelude::*;use sp_std::{vec, vec::Vec};--#[pallet::config]pub trait Config: frame_system::Config + CreateSignedTransaction<Call<Self>> {/ // Because this pallet emits events, it depends on the runtime's definition of an event.type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;type AuthorityId: AppCrypto<Self ::Public, Self::Signature>;}#[pallet::call]impl<T: Config> Pallet<T> {--// Define functions on the chain, submit variables, like the above function, how to Write as you want#[pallet::weight(0)]pub fn submit_data(origin: OriginFor<T>,payload: Vec<u8>) -> DispatchResultWithPostInfo {let _who = ensure_signed(origin)?;log::info!("in submit_data call: {:?}", payload);Ok(().into())} }#[pallet::hooks]impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {fn offchain_worker(block_number: T::BlockNumber) {log::info!("Hello World from offchain workers !: {:?}", block_number);// Obtain the variable to be submitted through the offchain worker, which can be obtained from the outside let payload: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];// Execute auxiliary function _ = Self::send_signed_tx(payload);log::info!("Leave from offchain workers!: {:?}", block_number);}fn on_initialize(_n: T: :BlockNumber) -> Weight {log::info!("in on_initialize!");let weight: Weight = Default::default();weight}fn on_finalize(_n: T::BlockNumber) {log::info! ("in on_finalize!");}fn on_idle(_n: T::BlockNumber,_remaining_weight: Weight) -> Weight {log::info!("in on_idle!");let weight: Weight = Default::default();weight}}impl<T: Config> Pallet<T> {fn send_signed_tx(payload: Vec<u8>) -> Result<(), &'static str> {let signer = Signer::<T, T::AuthorityId>::all_accounts();if !signer.can_sign() {return Err("No local accounts available. Consider adding one via `author_insertKey` RPC.",)}// 在辅助函数中调用 链上函数let results = signer.send_signed_transaction(|_account| Call::submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}let weight: Weight = Default::default();weight}}impl<T: Config> Pallet<T> {fn send_signed_tx(payload: Vec<u8>) -> Result<(), &'static str> {let signer = Signer::<T, T::AuthorityId>::all_accounts();if !signer.can_sign() {return Err("No local accounts available. Consider adding one via `author_insertKey` RPC.",)}// 在辅助函数中调用 链上函数let results = signer.send_signed_transaction(|_account| Call::submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}let weight: Weight = Default::default();weight}}impl<T: Config> Pallet<T> {fn send_signed_tx(payload: Vec<u8>) -> Result<(), &'static str> {let signer = Signer::<T, T::AuthorityId>::all_accounts();if !signer.can_sign() {return Err("No local accounts available. Consider adding one via `author_insertKey` RPC.",)}// 在辅助函数中调用 链上函数let results = signer.send_signed_transaction(|_account| Call::submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}'static str> {let signer = Signer::<T, T::AuthorityId>::all_accounts();if !signer.can_sign() {return Err("No local accounts available. Consider adding one via `author_insertKey` RPC.",)}// 在辅助函数中调用 链上函数let results = signer.send_signed_transaction(|_account| Call::submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}'static str> {let signer = Signer::<T, T::AuthorityId>::all_accounts();if !signer.can_sign() {return Err("No local accounts available. Consider adding one via `author_insertKey` RPC.",)}// 在辅助函数中调用 链上函数let results = signer.send_signed_transaction(|_account| Call::submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}submit_data { payload: payload.clone() });for (acc, res) in &results {match res {Ok(()) => log::info!("[{:?}] Submitted data:{:?}", acc.id, payload),Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),}}Ok(())}}}

3.substrate-node-template/pallets/template/Cargo.toml

[dependencies]--sp-core = { version ="6.0.0",default-features = false,git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" }

Compile and run

focus:

  1. Configure three files of offchain worker, node, runtime, template

  2. Offchain logic: on-chain transaction function - subpackaged and called in auxiliary functions - offchain passes parameters and calls auxiliary functions

  3. Transactions initiated in the current block are executed in the next block

Case 8: Send an unsigned transaction to the chain

Purpose

  1. No need to modify Node (do not configure account for signature payment transaction fee), only need to modify runtime, pallet files

  2. mostly boilerplate code

  3. Learn macros#[pallet::validate_unsigned], TransactionValidity, ValidTransaction, ensure_none, etc.

  4. It is not recommended to send too many unsigned transactions per block

change configuration

substrate-node-template-case/runtime/src/lib.rs

++++use super::*;impl<C> frame_system::offchain::SendTransactionTypes<C> for RuntimewhereRuntimeCall: From<C>,{type Extrinsic = UncheckedExtrinsic;type OverarchingCall = RuntimeCall;}

substrate-node-template-case/pallets/template/Cargo.toml

[dependencies]--sp-runtime = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" }log = { version = "0.4.17", default-features = false }[features]default = ["std"]std = [--"sp-runtime/std",]

substrate-node-template-case/pallets/template/src/lib.rs

use frame_system::offchain::{   SubmitTransaction,};use sp_runtime::{   transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction},};
#[pallet::config]pub trait Config:frame_system::Config + frame_system::offchain::SendTransactionTypes<Call<Self>>{/// Because this pallet emits events, it depends on the runtime's definition of an event.type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;}
#[pallet::call]impl<T: Config> Pallet<T> {--#[pallet::weight(0)]pub fn submit_data_unsigned(origin: OriginFor<T>, n: u64) -> DispatchResult {ensure_none(origin)?;log::info!("in submit_data_unsigned: {:?}", n);// Return a successful DispatchResultWithPostInfoOk(())}}#[pallet::hooks]impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {fn offchain_worker(block_number: T::BlockNumber) {log::info!("Hello World from offchain workers!: {:?}", block_number);let value: u64 = 42;let call = Call::submit_data_unsigned { n: value };_ = SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(call.into()).map_err(|_| {log::error!("Failed in offchain_unsigned_tx");},);log::info!("Leave from offchain workers!: {:?}", block_number);}}#[pallet::validate_unsigned]impl<T: Config> ValidateUnsigned for Pallet<T> {type Call = Call<T>;fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {if let Call::submit_data_unsigned { n: _ } = call {//let provide = b"submit_xxx_unsigned".to_vec();ValidTransaction::with_tag_prefix("ExampleOffchainWorker").priority(10000).and_provides(1).longevity(3).propagate(true).build()} else {InvalidTransaction::Call.into()}}}impl<T: Config> Pallet<T> {// 暂未写业务逻辑}with_tag_prefix("ExampleOffchainWorker").priority(10000).and_provides(1).longevity(3).propagate(true).build()} else {InvalidTransaction::Call.into()}}}impl<T: Config> Pallet<T> {// Business logic not yet written}with_tag_prefix("ExampleOffchainWorker").priority(10000).and_provides(1).longevity(3).propagate(true).build()} else {InvalidTransaction::Call.into()}}}impl<T: Config> Pallet<T> {// Business logic not yet written}

compile and run

As you can see, we have successfully submitted the unsigned transaction

For more cases, please refer to the official complete implementation

https://github.com/paritytech/substrate/blob/master/frame/examples/offchain-worker/src/lib.rs

Case 9: Use the Offchain Indexing feature to write data from the chain to the Offchain Storage

In this section, we use a relatively complete case implementation, see the code for details:

https://github.com/shiyivei/substrate-advanced-course/tree/master/lesson4/backend/pallets/kitties

The branch = "polkadot-v0.9.30" version is slightly different from the branch = "polkadot-v0.9.28" configuration, but the logic is the same. This part of the content will be filled later. Been busy again recently. OK, we will come to an end with the introduction of Offchain workers. see next topic

Guess you like

Origin blog.csdn.net/weixin_51487151/article/details/127523374