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 ¤t_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}