referrerpolicy=no-referrer-when-downgrade

pallet_example_offchain_worker/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: MIT-0
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy of
7// this software and associated documentation files (the "Software"), to deal in
8// the Software without restriction, including without limitation the rights to
9// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10// of the Software, and to permit persons to whom the Software is furnished to do
11// so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in all
14// copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23
24//! <!-- markdown-link-check-disable -->
25//! # Offchain Worker Example Pallet
26//!
27//! The Offchain Worker Example: A simple pallet demonstrating
28//! concepts, APIs and structures common to most offchain workers.
29//!
30//! Run `cargo doc --package pallet-example-offchain-worker --open` to view this module's
31//! documentation.
32//!
33//! - [`Config`]
34//! - [`Call`]
35//! - [`Pallet`]
36//!
37//! **This pallet serves as an example showcasing Substrate off-chain worker and is not meant to
38//! be used in production.**
39//!
40//! ## Overview
41//!
42//! In this example we are going to build a very simplistic, naive and definitely NOT
43//! production-ready oracle for BTC/USD price.
44//! Offchain Worker (OCW) will be triggered after every block, fetch the current price
45//! and prepare either signed or general transaction to feed the result back on chain.
46//! The on-chain logic will simply aggregate the results and store last `64` values to compute
47//! the average price.
48//! Additional logic in OCW is put in place to prevent spamming the network with both signed
49//! and general transactions. The pallet uses the `#[pallet::authorize]` attribute to validate
50//! general transactions, ensuring that only one general transaction can be accepted per
51//! `AuthorizedTxInterval` blocks.
52
53#![cfg_attr(not(feature = "std"), no_std)]
54
55extern crate alloc;
56
57use alloc::vec::Vec;
58use codec::{Decode, DecodeWithMemTracking, Encode};
59use frame_support::{traits::Get, weights::Weight};
60use frame_system::{
61	self as system,
62	offchain::{
63		AppCrypto, CreateAuthorizedTransaction, CreateSignedTransaction, SendSignedTransaction,
64		SignedPayload, Signer, SigningTypes, SubmitTransaction,
65	},
66	pallet_prelude::BlockNumberFor,
67};
68use lite_json::json::JsonValue;
69use sp_core::crypto::KeyTypeId;
70use sp_runtime::{
71	offchain::{
72		http,
73		storage::{MutateStorageError, StorageRetrievalError, StorageValueRef},
74		Duration,
75	},
76	traits::Zero,
77	transaction_validity::{
78		InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityWithRefund,
79		ValidTransaction,
80	},
81	Debug,
82};
83
84#[cfg(test)]
85mod tests;
86
87/// Defines application identifier for crypto keys of this module.
88///
89/// Every module that deals with signatures needs to declare its unique identifier for
90/// its crypto keys.
91/// When offchain worker is signing transactions it's going to request keys of type
92/// `KeyTypeId` from the keystore and use the ones it finds to sign the transaction.
93/// The keys can be inserted manually via RPC (see `author_insertKey`).
94pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"btc!");
95
96/// Based on the above `KeyTypeId` we need to generate a pallet-specific crypto type wrappers.
97/// We can use from supported crypto kinds (`sr25519`, `ed25519` and `ecdsa`) and augment
98/// the types with this pallet-specific identifier.
99pub mod crypto {
100	use super::KEY_TYPE;
101	use sp_core::sr25519::Signature as Sr25519Signature;
102	use sp_runtime::{
103		app_crypto::{app_crypto, sr25519},
104		traits::Verify,
105		MultiSignature, MultiSigner,
106	};
107	app_crypto!(sr25519, KEY_TYPE);
108
109	pub struct TestAuthId;
110
111	impl frame_system::offchain::AppCrypto<MultiSigner, MultiSignature> for TestAuthId {
112		type RuntimeAppPublic = Public;
113		type GenericSignature = sp_core::sr25519::Signature;
114		type GenericPublic = sp_core::sr25519::Public;
115	}
116
117	// implemented for mock runtime in test
118	impl frame_system::offchain::AppCrypto<<Sr25519Signature as Verify>::Signer, Sr25519Signature>
119		for TestAuthId
120	{
121		type RuntimeAppPublic = Public;
122		type GenericSignature = sp_core::sr25519::Signature;
123		type GenericPublic = sp_core::sr25519::Public;
124	}
125}
126
127pub use pallet::*;
128
129#[frame_support::pallet]
130pub mod pallet {
131	use super::*;
132	use frame_support::pallet_prelude::*;
133	use frame_system::pallet_prelude::*;
134
135	/// This pallet's configuration trait
136	///
137	/// # Requirements
138	///
139	/// This pallet requires `frame_system::AuthorizeCall` to be included in the runtime's
140	/// transaction extension pipeline.
141	/// The integrity test will verify this at runtime.
142	#[pallet::config]
143	pub trait Config:
144		CreateSignedTransaction<Call<Self>>
145		+ CreateAuthorizedTransaction<Call<Self>>
146		+ frame_system::Config
147	{
148		/// The identifier type for an offchain worker.
149		type AuthorityId: AppCrypto<Self::Public, Self::Signature>;
150
151		// Configuration parameters
152
153		/// A grace period after we send transaction.
154		///
155		/// To avoid sending too many transactions, we only attempt to send one
156		/// every `GRACE_PERIOD` blocks. We use Local Storage to coordinate
157		/// sending between distinct runs of this offchain worker.
158		#[pallet::constant]
159		type GracePeriod: Get<BlockNumberFor<Self>>;
160
161		/// Number of blocks of cooldown after an authorized transaction is included.
162		///
163		/// This ensures that we only accept authorized transactions once, every
164		/// `AuthorizedTxInterval` blocks.
165		#[pallet::constant]
166		type AuthorizedTxInterval: Get<BlockNumberFor<Self>>;
167
168		/// A configuration for base priority of authorized transactions.
169		///
170		/// This is exposed so that it can be tuned for particular runtime, when
171		/// multiple pallets send authorized transactions.
172		#[pallet::constant]
173		type AuthorizedTxPriority: Get<TransactionPriority>;
174
175		/// Maximum number of prices.
176		#[pallet::constant]
177		type MaxPrices: Get<u32>;
178	}
179
180	#[pallet::pallet]
181	pub struct Pallet<T>(_);
182
183	#[pallet::hooks]
184	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
185		/// Integrity test to ensure AuthorizeCall is configured in the runtime.
186		///
187		/// This pallet uses the `#[pallet::authorize]` attribute on calls that require
188		/// authorization validation. For these calls to work properly, the runtime must
189		/// include `frame_system::AuthorizeCall` as part of its transaction extension pipeline.
190		///
191		/// This test validates that the Block type's Extrinsic includes the necessary
192		/// transaction extension structure by checking the type name.
193		fn integrity_test() {
194			use sp_runtime::traits::Block as BlockT;
195
196			// Get the full type name of the Block's Extrinsic type
197			let extrinsic_type_name = core::any::type_name::<<T::Block as BlockT>::Extrinsic>();
198			let extension_type_name = core::any::type_name::<frame_system::AuthorizeCall<T>>();
199
200			// Verify that AuthorizeCall is present in the extrinsic type
201			// The extrinsic should contain the AuthorizeCall extension in its structure
202			assert!(
203				extrinsic_type_name.contains(extension_type_name),
204				"The runtime must include `frame_system::AuthorizeCall` in its transaction extension \
205				pipeline for this pallet to work correctly. The pallet uses `#[pallet::authorize]` \
206				which requires AuthorizeCall to validate authorized transactions. \
207				Current extrinsic type: {}",
208				extrinsic_type_name
209			);
210		}
211
212		/// Offchain Worker entry point.
213		///
214		/// By implementing `fn offchain_worker` you declare a new offchain worker.
215		/// This function will be called when the node is fully synced and a new best block is
216		/// successfully imported.
217		/// Note that it's not guaranteed for offchain workers to run on EVERY block, there might
218		/// be cases where some blocks are skipped, or for some the worker runs twice (re-orgs),
219		/// so the code should be able to handle that.
220		/// You can use `Local Storage` API to coordinate runs of the worker.
221		fn offchain_worker(block_number: BlockNumberFor<T>) {
222			// Note that having logs compiled to WASM may cause the size of the blob to increase
223			// significantly. You can use `Debug` custom derive to hide details of the types
224			// in WASM. The `sp-api` crate also provides a feature `disable-logging` to disable
225			// all logging and thus, remove any logging from the WASM.
226			log::info!("Hello World from offchain workers!");
227
228			// Since off-chain workers are just part of the runtime code, they have direct access
229			// to the storage and other included pallets.
230			//
231			// We can easily import `frame_system` and retrieve a block hash of the parent block.
232			let parent_hash = <system::Pallet<T>>::block_hash(block_number - 1u32.into());
233			log::debug!("Current block: {:?} (parent hash: {:?})", block_number, parent_hash);
234
235			// It's a good practice to keep `fn offchain_worker()` function minimal, and move most
236			// of the code to separate `impl` block.
237			// Here we call a helper function to calculate current average price.
238			// This function reads storage entries of the current state.
239			let average: Option<u32> = Self::average_price();
240			log::debug!("Current price: {:?}", average);
241
242			// For this example we are going to send both signed and general transactions
243			// depending on the block number.
244			// Usually it's enough to choose one or the other.
245			let should_send = Self::choose_transaction_type(block_number);
246			let res = match should_send {
247				TransactionType::Signed => Self::fetch_price_and_send_signed(),
248				TransactionType::AuthorizedForAny => {
249					Self::fetch_price_and_send_authorized_tx_for_any_account(block_number)
250				},
251				TransactionType::AuthorizedForAll => {
252					Self::fetch_price_and_send_authorized_tx_for_all_accounts(block_number)
253				},
254				TransactionType::Raw => Self::fetch_price_and_send_raw_authorized(block_number),
255				TransactionType::None => Ok(()),
256			};
257			if let Err(e) = res {
258				log::error!("Error: {}", e);
259			}
260		}
261	}
262
263	/// A public part of the pallet.
264	#[pallet::call]
265	impl<T: Config> Pallet<T> {
266		/// Submit new price to the list.
267		///
268		/// This method is a public function of the module and can be called from within
269		/// a transaction. It appends given `price` to current list of prices.
270		/// In our example the `offchain worker` will create, sign & submit a transaction that
271		/// calls this function passing the price.
272		///
273		/// The transaction needs to be signed (see `ensure_signed`) check, so that the caller
274		/// pays a fee to execute it.
275		/// This makes sure that it's not easy (or rather cheap) to attack the chain by submitting
276		/// excessive transactions, but note that it doesn't ensure the price oracle is actually
277		/// working and receives (and provides) meaningful data.
278		/// This example is not focused on correctness of the oracle itself, but rather its
279		/// purpose is to showcase offchain worker capabilities.
280		#[pallet::call_index(0)]
281		#[pallet::weight({0})]
282		pub fn submit_price(origin: OriginFor<T>, price: u32) -> DispatchResultWithPostInfo {
283			// Retrieve sender of the transaction.
284			let who = ensure_signed(origin)?;
285			// Add the price to the on-chain list.
286			Self::add_price(Some(who), price);
287			Ok(().into())
288		}
289
290		/// Submit new price to the list via general transaction.
291		///
292		/// Works exactly like the `submit_price` function, but since we allow sending the
293		/// transaction without a signature, and hence without paying any fees,
294		/// we need a way to make sure that only some transactions are accepted.
295		/// This function can be called only once every `T::AuthorizedTxInterval` blocks.
296		///
297		/// Transactions are de-duplicated on the pool level via the `provides` tag in the
298		/// validation logic (`validate_transaction_parameters`), which returns
299		/// `next_authorized_at`. This ensures only one transaction per interval can be included
300		/// in a block.
301		///
302		/// It's important to specify `weight` for authorized calls as well, because even though
303		/// they don't charge fees, we still don't want a single block to contain unlimited
304		/// number of such transactions.
305		///
306		/// This example is not focused on correctness of the oracle itself, but rather its
307		/// purpose is to showcase offchain worker capabilities.
308		#[pallet::call_index(1)]
309		#[pallet::weight({0})]
310		// Minimal weight since validation is lightweight. But in real-world scenarios, this should
311		// be benchmarked.
312		#[pallet::weight_of_authorize(Weight::from_parts(5_000, 0))]
313		#[pallet::authorize(|
314			_source: TransactionSource,
315			block_number: &BlockNumberFor<T>,
316			new_price: &u32,
317		| -> TransactionValidityWithRefund {
318			Pallet::<T>::validate_transaction_parameters(block_number, new_price)
319				.map(|v| (v, /* no refund */ Weight::zero()))
320		})]
321		pub fn submit_price_authorized(
322			origin: OriginFor<T>,
323			_block_number: BlockNumberFor<T>,
324			price: u32,
325		) -> DispatchResultWithPostInfo {
326			ensure_authorized(origin)?;
327			// Add the price to the on-chain list, but mark it as coming from an empty address.
328			Self::add_price(None, price);
329			// now increment the block number at which we expect next authorized transaction.
330			let current_block = <system::Pallet<T>>::block_number();
331			<NextAuthorizedAt<T>>::put(current_block + T::AuthorizedTxInterval::get());
332			Ok(().into())
333		}
334
335		#[pallet::call_index(2)]
336		#[pallet::weight({0})]
337		// Minimal weight since validation is lightweight. But in real-world scenarios, this should
338		// be benchmarked.
339		#[pallet::weight_of_authorize(Weight::from_parts(5_000, 0))]
340		#[pallet::authorize(|
341			_source: TransactionSource,
342			price_payload: &PricePayload<T::Public, BlockNumberFor<T>>,
343			signature: &T::Signature,
344		| -> TransactionValidityWithRefund {
345			let signature_valid = SignedPayload::<T>::verify::<T::AuthorityId>(price_payload, signature.clone());
346			if !signature_valid {
347				return Err(InvalidTransaction::BadProof.into())
348			}
349			Pallet::<T>::validate_transaction_parameters(&price_payload.block_number, &price_payload.price)
350				.map(|v| (v, /* no refund */ Weight::zero()))
351		})]
352		pub fn submit_price_authorized_with_signed_payload(
353			origin: OriginFor<T>,
354			price_payload: PricePayload<T::Public, BlockNumberFor<T>>,
355			_signature: T::Signature,
356		) -> DispatchResultWithPostInfo {
357			ensure_authorized(origin)?;
358			// Add the price to the on-chain list, but mark it as coming from an empty address.
359			Self::add_price(None, price_payload.price);
360			// now increment the block number at which we expect next authorized transaction.
361			let current_block = <system::Pallet<T>>::block_number();
362			<NextAuthorizedAt<T>>::put(current_block + T::AuthorizedTxInterval::get());
363			Ok(().into())
364		}
365	}
366
367	/// Events for the pallet.
368	#[pallet::event]
369	#[pallet::generate_deposit(pub(super) fn deposit_event)]
370	pub enum Event<T: Config> {
371		/// Event generated when new price is accepted to contribute to the average.
372		NewPrice { price: u32, maybe_who: Option<T::AccountId> },
373	}
374
375	/// A vector of recently submitted prices.
376	///
377	/// This is used to calculate average price, should have bounded size.
378	#[pallet::storage]
379	pub(super) type Prices<T: Config> = StorageValue<_, BoundedVec<u32, T::MaxPrices>, ValueQuery>;
380
381	/// Defines the block when next authorized transaction will be accepted.
382	///
383	/// To prevent spam of authorized (and unpaid!) transactions on the network,
384	/// we only allow one transaction every `T::AuthorizedTxInterval` blocks.
385	/// This storage entry defines when new transaction is going to be accepted.
386	#[pallet::storage]
387	pub(super) type NextAuthorizedAt<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;
388}
389
390/// Payload used by this example crate to hold price
391/// data required to submit a transaction.
392#[derive(
393	Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug, scale_info::TypeInfo,
394)]
395pub struct PricePayload<Public, BlockNumber> {
396	block_number: BlockNumber,
397	price: u32,
398	public: Public,
399}
400
401impl<T: SigningTypes> SignedPayload<T> for PricePayload<T::Public, BlockNumberFor<T>> {
402	fn public(&self) -> T::Public {
403		self.public.clone()
404	}
405}
406
407enum TransactionType {
408	Signed,
409	AuthorizedForAny,
410	AuthorizedForAll,
411	Raw,
412	None,
413}
414
415impl<T: Config> Pallet<T> {
416	/// Chooses which transaction type to send.
417	///
418	/// This function serves mostly to showcase `StorageValue` helper
419	/// and local storage usage.
420	///
421	/// Returns a type of transaction that should be produced in current run.
422	fn choose_transaction_type(block_number: BlockNumberFor<T>) -> TransactionType {
423		/// A friendlier name for the error that is going to be returned in case we are in the grace
424		/// period.
425		const RECENTLY_SENT: () = ();
426
427		// Start off by creating a reference to Local Storage value.
428		// Since the local storage is common for all offchain workers, it's a good practice
429		// to prepend your entry with the module name.
430		let val = StorageValueRef::persistent(b"example_ocw::last_send");
431		// The Local Storage is persisted and shared between runs of the offchain workers,
432		// and offchain workers may run concurrently. We can use the `mutate` function, to
433		// write a storage entry in an atomic fashion. Under the hood it uses `compare_and_set`
434		// low-level method of local storage API, which means that only one worker
435		// will be able to "acquire a lock" and send a transaction if multiple workers
436		// happen to be executed concurrently.
437		let res =
438			val.mutate(|last_send: Result<Option<BlockNumberFor<T>>, StorageRetrievalError>| {
439				match last_send {
440					// If we already have a value in storage and the block number is recent enough
441					// we avoid sending another transaction at this time.
442					Ok(Some(block)) if block_number < block + T::GracePeriod::get() => {
443						Err(RECENTLY_SENT)
444					},
445					// In every other case we attempt to acquire the lock and send a transaction.
446					_ => Ok(block_number),
447				}
448			});
449
450		// The result of `mutate` call will give us a nested `Result` type.
451		// The first one matches the return of the closure passed to `mutate`, i.e.
452		// if we return `Err` from the closure, we get an `Err` here.
453		// In case we return `Ok`, here we will have another (inner) `Result` that indicates
454		// if the value has been set to the storage correctly - i.e. if it wasn't
455		// written to in the meantime.
456		match res {
457			// The value has been set correctly, which means we can safely send a transaction now.
458			Ok(block_number) => {
459				// We will send different transactions based on a random number.
460				// Note that this logic doesn't really guarantee that the transactions will be sent
461				// in an alternating fashion (i.e. fairly distributed). Depending on the execution
462				// order and lock acquisition, we may end up for instance sending two `Signed`
463				// transactions in a row. If a strict order is desired, it's better to use
464				// the storage entry for that. (for instance store both block number and a flag
465				// indicating the type of next transaction to send).
466				let transaction_type = block_number % 4u32.into();
467				if transaction_type == Zero::zero() {
468					TransactionType::Signed
469				} else if transaction_type == BlockNumberFor::<T>::from(1u32) {
470					TransactionType::AuthorizedForAny
471				} else if transaction_type == BlockNumberFor::<T>::from(2u32) {
472					TransactionType::AuthorizedForAll
473				} else {
474					TransactionType::Raw
475				}
476			},
477			// We are in the grace period, we should not send a transaction this time.
478			Err(MutateStorageError::ValueFunctionFailed(RECENTLY_SENT)) => TransactionType::None,
479			// We wanted to send a transaction, but failed to write the block number (acquire a
480			// lock). This indicates that another offchain worker that was running concurrently
481			// most likely executed the same logic and succeeded at writing to storage.
482			// Thus we don't really want to send the transaction, knowing that the other run
483			// already did.
484			Err(MutateStorageError::ConcurrentModification(_)) => TransactionType::None,
485		}
486	}
487
488	/// A helper function to fetch the price and send signed transaction.
489	fn fetch_price_and_send_signed() -> Result<(), &'static str> {
490		let signer = Signer::<T, T::AuthorityId>::all_accounts();
491		if !signer.can_sign() {
492			return Err(
493				"No local accounts available. Consider adding one via `author_insertKey` RPC.",
494			);
495		}
496		// Make an external HTTP request to fetch the current price.
497		// Note this call will block until response is received.
498		let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
499
500		// Using `send_signed_transaction` associated type we create and submit a transaction
501		// representing the call, we've just created.
502		// Submit signed will return a vector of results for all accounts that were found in the
503		// local keystore with expected `KEY_TYPE`.
504		let results = signer.send_signed_transaction(|_account| {
505			// Received price is wrapped into a call to `submit_price` public function of this
506			// pallet. This means that the transaction, when executed, will simply call that
507			// function passing `price` as an argument.
508			Call::submit_price { price }
509		});
510
511		for (acc, res) in &results {
512			match res {
513				Ok(()) => log::info!("[{:?}] Submitted price of {} cents", acc.id, price),
514				Err(e) => log::error!("[{:?}] Failed to submit transaction: {:?}", acc.id, e),
515			}
516		}
517
518		Ok(())
519	}
520
521	/// A helper function to fetch the price and send a raw authorized transaction.
522	fn fetch_price_and_send_raw_authorized(
523		block_number: BlockNumberFor<T>,
524	) -> Result<(), &'static str> {
525		// Make sure we don't fetch the price if the authorized transaction is going to be rejected
526		// anyway.
527		let next_authorized_at = NextAuthorizedAt::<T>::get();
528		if next_authorized_at > block_number {
529			return Err("Too early to send authorized transaction");
530		}
531
532		// Make an external HTTP request to fetch the current price.
533		// Note this call will block until response is received.
534		let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
535
536		// Received price is wrapped into a call to `submit_price_authorized` public function of
537		// this pallet. This means that the transaction, when executed, will simply call that
538		// function passing `price` as an argument.
539		let call = Call::submit_price_authorized { block_number, price };
540
541		// Now let's create a transaction out of this call and submit it to the pool.
542		// Here we showcase two ways to send a general transaction / unsigned payload (raw)
543		//
544		// By default general transactions start the transaction extension pipeline with the origin
545		// `None`. We define custom validation logic using the `#[pallet::authorize]` attribute.
546		// This custom validation is executed by the transaction extension `AuthorizeCall`, which
547		// will change the origin to `frame_system::Origin::Authorized` if validation succeeds.
548		// Note that it's EXTREMELY important to carefully implement custom validation logic, as
549		// any mistakes can lead to opening DoS or spam attack vectors. See validation logic docs
550		// for more details.
551		//
552		let xt = T::create_authorized_transaction(call.into());
553		SubmitTransaction::<T, Call<T>>::submit_transaction(xt)
554			.map_err(|()| "Unable to submit transaction.")?;
555
556		Ok(())
557	}
558
559	/// A helper function to fetch the price, sign payload and send an authorized transaction
560	fn fetch_price_and_send_authorized_tx_for_any_account(
561		block_number: BlockNumberFor<T>,
562	) -> Result<(), &'static str> {
563		// Make sure we don't fetch the price if the authorized transaction is going to be rejected
564		// anyway.
565		let next_authorized_at = NextAuthorizedAt::<T>::get();
566		if next_authorized_at > block_number {
567			return Err("Too early to send authorized transaction");
568		}
569
570		// Make an external HTTP request to fetch the current price.
571		// Note this call will block until response is received.
572		let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
573
574		// Sign using any account and create an authorized transaction
575		let signer = Signer::<T, T::AuthorityId>::any_account();
576
577		// Get the first available account
578		let account = signer.accounts_from_keys().next().ok_or("No local accounts available.")?;
579
580		// Create the payload to sign
581		let payload = PricePayload { price, block_number, public: account.public.clone() };
582
583		// Sign the payload
584		let signature = <PricePayload<
585			<T as SigningTypes>::Public,
586			BlockNumberFor<T>,
587		> as SignedPayload<T>>::sign::<T::AuthorityId>(&payload)
588			.ok_or("Failed to sign payload")?;
589
590		// Create the call with the signed payload
591		let call =
592			Call::submit_price_authorized_with_signed_payload { price_payload: payload, signature };
593
594		// Create an authorized transaction and submit it
595		let xt = T::create_authorized_transaction(call.into());
596		SubmitTransaction::<T, Call<T>>::submit_transaction(xt)
597			.map_err(|()| "Unable to submit transaction")?;
598
599		Ok(())
600	}
601
602	/// A helper function to fetch the price, sign payload and send an authorized transaction
603	fn fetch_price_and_send_authorized_tx_for_all_accounts(
604		block_number: BlockNumberFor<T>,
605	) -> Result<(), &'static str> {
606		// Make sure we don't fetch the price if the authorized transaction is going to be rejected
607		// anyway.
608		let next_authorized_at = NextAuthorizedAt::<T>::get();
609		if next_authorized_at > block_number {
610			return Err("Too early to send authorized transaction");
611		}
612
613		// Make an external HTTP request to fetch the current price.
614		// Note this call will block until response is received.
615		let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
616
617		// -- Sign using all accounts and create authorized transactions
618		let signer = Signer::<T, T::AuthorityId>::all_accounts();
619
620		// Iterate over all available accounts
621		for account in signer.accounts_from_keys() {
622			// Create the payload to sign
623			let payload = PricePayload { price, block_number, public: account.public.clone() };
624
625			// Sign the payload
626			let signature =
627				<PricePayload<<T as SigningTypes>::Public, BlockNumberFor<T>> as SignedPayload<
628					T,
629				>>::sign::<T::AuthorityId>(&payload)
630				.ok_or("Failed to sign payload")?;
631
632			// Create the call with the signed payload
633			let call = Call::submit_price_authorized_with_signed_payload {
634				price_payload: payload,
635				signature,
636			};
637
638			// Create an authorized transaction and submit it
639			let xt = T::create_authorized_transaction(call.into());
640			SubmitTransaction::<T, Call<T>>::submit_transaction(xt)
641				.map_err(|()| "Unable to submit transaction")?;
642		}
643
644		Ok(())
645	}
646
647	/// Fetch current price and return the result in cents.
648	fn fetch_price() -> Result<u32, http::Error> {
649		// We want to keep the offchain worker execution time reasonable, so we set a hard-coded
650		// deadline to 2s to complete the external call.
651		// You can also wait indefinitely for the response, however you may still get a timeout
652		// coming from the host machine.
653		let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(2_000));
654		// Initiate an external HTTP GET request.
655		// This is using high-level wrappers from `sp_runtime`, for the low-level calls that
656		// you can find in `sp_io`. The API is trying to be similar to `request`, but
657		// since we are running in a custom WASM execution environment we can't simply
658		// import the library here.
659		let request =
660			http::Request::get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD");
661		// We set the deadline for sending of the request, note that awaiting response can
662		// have a separate deadline. Next we send the request, before that it's also possible
663		// to alter request headers or stream body content in case of non-GET requests.
664		let pending = request.deadline(deadline).send().map_err(|_| http::Error::IoError)?;
665
666		// The request is already being processed by the host, we are free to do anything
667		// else in the worker (we can send multiple concurrent requests too).
668		// At some point however we probably want to check the response though,
669		// so we can block current thread and wait for it to finish.
670		// Note that since the request is being driven by the host, we don't have to wait
671		// for the request to have it complete, we will just not read the response.
672		let response = pending.try_wait(deadline).map_err(|_| http::Error::DeadlineReached)??;
673		// Let's check the status code before we proceed to reading the response.
674		if response.code != 200 {
675			log::warn!("Unexpected status code: {}", response.code);
676			return Err(http::Error::Unknown);
677		}
678
679		// Next we want to fully read the response body and collect it to a vector of bytes.
680		// Note that the return object allows you to read the body in chunks as well
681		// with a way to control the deadline.
682		let body = response.body().collect::<Vec<u8>>();
683
684		// Create a str slice from the body.
685		let body_str = alloc::str::from_utf8(&body).map_err(|_| {
686			log::warn!("No UTF8 body");
687			http::Error::Unknown
688		})?;
689
690		let price = match Self::parse_price(body_str) {
691			Some(price) => Ok(price),
692			None => {
693				log::warn!("Unable to extract price from the response: {:?}", body_str);
694				Err(http::Error::Unknown)
695			},
696		}?;
697
698		log::warn!("Got price: {} cents", price);
699
700		Ok(price)
701	}
702
703	/// Parse the price from the given JSON string using `lite-json`.
704	///
705	/// Returns `None` when parsing failed or `Some(price in cents)` when parsing is successful.
706	fn parse_price(price_str: &str) -> Option<u32> {
707		let val = lite_json::parse_json(price_str);
708		let price = match val.ok()? {
709			JsonValue::Object(obj) => {
710				let (_, v) = obj.into_iter().find(|(k, _)| k.iter().copied().eq("USD".chars()))?;
711				match v {
712					JsonValue::Number(number) => number,
713					_ => return None,
714				}
715			},
716			_ => return None,
717		};
718
719		let exp = price.fraction_length.saturating_sub(2);
720		Some(price.integer as u32 * 100 + (price.fraction / 10_u64.pow(exp)) as u32)
721	}
722
723	/// Add new price to the list.
724	fn add_price(maybe_who: Option<T::AccountId>, price: u32) {
725		log::info!("Adding to the average: {}", price);
726		<Prices<T>>::mutate(|prices| {
727			if prices.try_push(price).is_err() {
728				prices[(price % T::MaxPrices::get()) as usize] = price;
729			}
730		});
731
732		let average = Self::average_price()
733			.expect("The average is not empty, because it was just mutated; qed");
734		log::info!("Current average price is: {}", average);
735		// here we are raising the NewPrice event
736		Self::deposit_event(Event::NewPrice { price, maybe_who });
737	}
738
739	/// Calculate current average price.
740	fn average_price() -> Option<u32> {
741		let prices = Prices::<T>::get();
742		if prices.is_empty() {
743			None
744		} else {
745			Some(prices.iter().fold(0_u32, |a, b| a.saturating_add(*b)) / prices.len() as u32)
746		}
747	}
748
749	fn validate_transaction_parameters(
750		block_number: &BlockNumberFor<T>,
751		new_price: &u32,
752	) -> TransactionValidity {
753		// Now let's check if the transaction has any chance to succeed.
754		let next_authorized_at = NextAuthorizedAt::<T>::get();
755		if &next_authorized_at > block_number {
756			return InvalidTransaction::Stale.into();
757		}
758		// Let's make sure to reject transactions from the future.
759		let current_block = <system::Pallet<T>>::block_number();
760		if &current_block < block_number {
761			return InvalidTransaction::Future.into();
762		}
763
764		// We prioritize transactions that are more far away from current average.
765		//
766		// Note this doesn't make much sense when building an actual oracle, but this example
767		// is here mostly to show off offchain workers capabilities, not about building an
768		// oracle.
769		let avg_price = Self::average_price()
770			.map(|price| if &price > new_price { price - new_price } else { new_price - price })
771			.unwrap_or(0);
772
773		ValidTransaction::with_tag_prefix("ExampleOffchainWorker")
774			// We set base priority to 2**20 and hope it's included before any other
775			// transactions in the pool. Next we tweak the priority depending on how much
776			// it differs from the current average. (the more it differs the more priority it
777			// has).
778			.priority(T::AuthorizedTxPriority::get().saturating_add(avg_price as _))
779			// This transaction does not require anything else to go before into the pool.
780			// In theory we could require `previous_authorized_at` transaction to go first,
781			// but it's not necessary in our case.
782			//.and_requires()
783			// We set the `provides` tag to be the same as `next_authorized_at`. This makes
784			// sure only one transaction produced after `next_authorized_at` will ever
785			// get to the transaction pool and will end up in the block.
786			// We can still have multiple transactions compete for the same "spot",
787			// and the one with higher priority will replace other one in the pool.
788			.and_provides(next_authorized_at)
789			// The transaction is only valid for next 5 blocks. After that it's
790			// going to be revalidated by the pool.
791			.longevity(5)
792			// It's fine to propagate that transaction to other peers, which means it can be
793			// created even by nodes that don't produce blocks.
794			// Note that sometimes it's better to keep it for yourself (if you are the block
795			// producer), since for instance in some schemes others may copy your solution and
796			// claim a reward.
797			.propagate(true)
798			.build()
799	}
800}