infrablockchain-docs
en
en
  • InfraBlockchain
    • Learn
      • Architecture
        • Architecture
        • Network Participants
        • Parachain
          • System Parachains
      • Protocol
        • System Token
        • Transaction Fee
        • Proof of Transaction
      • Substrate
        • Learn
          • Basic
            • Cryptography
            • Blockchain Basics
            • Consensus
            • Networks and Nodes
            • Blockchain Transaction
            • Transaction Life Cycle
            • Offchain Operations
            • Light Client
            • Rust for Substrate
            • Introduction to Library
            • Architecture and Rust Libraries
            • File Architecture
            • Accounts, Addresses, and Keys
            • Transaction Format
            • Blockchain Randomness
          • FRAME
            • FRAME Pallets
            • FRAME Macros
            • Custom Pallets
            • Pallet Coupling
            • Origin
            • Events and Erros
            • Runtime Storage
            • State Transitions and Storage
            • SCALE Encoding
            • Weight and Fee
            • Runtime API
            • Runtime Development
          • Account
          • Address Format
          • Glossary
          • CLI
            • Archive
            • Memory Profiler
            • Node Template
            • sidecar
            • srtool
            • Subkey
            • subxt
            • try-runtime
            • tx-wrapper
          • Runtime Development
            • Basics
              • Configure Genesis State
              • Configure Runtime Constants
              • Customize a Chain Spec
              • Import a Pallet
              • Use Helper Function
            • Consensus Model
              • PoW
              • Create a Hybrid Node
            • Offchain Worker
              • Request Offchain HTTP
              • Offchain Indexing
              • Offchain Local Storage
            • Pallet Design
              • Create a Storage Structure
              • Implement Lockable Currency
              • Incorporate Randomness
              • Loose Coupling
              • Tight Coupling
            • Parachain Development
              • Add HRMP Channel
              • Add Paranodes
              • Connect to a Local Relay Chain
              • Convert a Solo Chain
              • Prepare to Launch
              • Select Collator
              • Upgrade a Parachain
            • Storage Migration
              • Basics
              • Trigger Migration
            • Test
              • Basics
              • Test a Transfer Transaction
            • Tools
              • Create a TxWrapper
              • Use Sidecar
              • try-runtime
              • Verify WASM
            • Weigths
              • Benchmark
              • Calculate Fees
              • Use Conditional Weights
              • Use Custom Weights
        • Build
          • Decide What to Build
          • Build Process
          • Determinisitc Runtime
          • Chain Spec
          • Genesis Configuration
          • Application Development
          • RPC
          • Troubleshoot Your Code
        • Tutorials
          • Install
            • Developer Tools
            • Linux
            • macOS
            • Rust Toolchain
            • Issues
            • Windows
          • Quick Start
            • Explore the Code
            • Modify Runtime
            • Start a Node
            • Substrate Basics
          • Build a Blockchain
            • Add Trusted Nodes
            • Authorize Specific Nodes
            • Build a Local Blockchain
            • Simulate Network
            • Upgrade a Running Network
          • Build Application Logic
            • Add a Pallet
            • Add Offchasin Workers
            • Publish Custom Pallets
            • Specify Origin for a Call
            • Use Macros in a Custom Pallet
          • Integrate with Tools
            • Access EVM Accounts
            • EVM Integration
            • Explore Sidecar Endpoints
            • Integrate a Light Client Node
          • Smart Contracts
            • Strategy
            • Build a Token Contract
            • Develop a Smart Contract
            • Prepare Your First Contract
            • Troubleshoot Smart Contracts
            • Use Maps for Storing Values
      • XCM
        • XCM
        • XCM Format
    • Service Chains
      • InfraDID
      • InfraEVM
      • URAuth(Universal Resource Auth)
    • DevOps
      • Build
      • Deploy
      • Monitoring
      • Runtime Upgrade
    • Tutorials
      • Basic
        • How to Interact with System Token
        • How To Pay Transaction Fee
        • How To Vote with TaaV
        • Hot to Get Validator Reward
      • Build
        • Build InfraRelayChain
        • Build Parachain
        • Open Message Passing Channels
        • Transfer Assets with XCM
      • Test
        • Benchmark
        • Check Runtime
        • Debug
        • Simulate Parachains
        • Unit Testing
      • Service Chains
        • Play with InfraDID
          • Build
          • Add Keys
          • Add Service Endpoint
          • Create InfraDID
        • Play with InfraEVM
          • Build
          • Deposit and Withdraw Token
          • Deploy ERC20 Contract
          • Deploy ERC721 Contract
          • Deploy ERC1155 Contract
  • Newnal Data Market
Powered by GitBook
On this page
  • Steps
  • Related material
  1. InfraBlockchain
  2. Learn
  3. Substrate
  4. Learn
  5. Runtime Development
  6. Offchain Worker

Offchain Indexing

PreviousRequest Offchain HTTPNextOffchain Local Storage

Last updated 1 year ago

This guide will step you through how to pass data from an extrinsic to an offchain worker without writing to storage.

Occasionally on-chain extrinsics need to pass data to offchain workers with predictable write behavior. This data could be written to on-chain storage for offchain workers to read, but this could potentially incur a huge cost to the blockchain. Another way of handing data from on-chain to offchain worlds is by saving it in the local storage of a node via offchain indexing.

Off-chain indexing is called from on-chain extrinsics which implies that the data written locally is expected to be consistent across all nodes in the network.

Another use case is when needing to store large chunks of data on-chain for offchain workers to process. This would be too expensive. The solution would be to use offchain indexing to store a hash of that data on-chain and have the corresponding raw data stored locally for an offchain worker to read later on.

Notice that the same extrinsic could be run multiple times when there are forked blocks. The consequence is that in case non-unique keys are used, the data might be overwritten by different forked blocks and the content in the local storage will be different between nodes. So developers should be careful in forming the right indexing key to prevent potential overwrites.

Note: In order to see the offchain indexing feature in action, run your Substrate node with the offchain indexing flag ON. For example: ./target/release/substrate-node --enable-offchain-indexing true

Steps

  1. Create a unique key used for indexing.

    In the src/lib.rs of your pallet:

    const ONCHAIN_TX_KEY: &[u8] = b"my_pallet::indexing1";
    
    #[pallet::call]
    impl<T: Config> Pallet<T> {
    	#[pallet::weight(100)]
    	pub fn extrinsic(origin: OriginFor<T>, number: u64) -> DispatchResult {
    		let who = ensure_signed(origin)?;
    
    		let key = Self::derived_key(frame_system::Module::<T>::block_number());
    		// ...
    
    		Ok(())
    	}
    }
    
    impl<T: Config> Pallet<T> {
    	fn derived_key(block_number: T::BlockNumber) -> Vec<u8> {
    		block_number.using_encoded(|encoded_bn| {
    			ONCHAIN_TX_KEY.clone().into_iter()
    				.chain(b"/".into_iter())
    				.chain(encoded_bn)
    				.copied()
    				.collect::<Vec<u8>>()
    		})
    	}
    }

    In the above code within a regular extrinsic, the Self::derived_key() helper method is called to generate the key used later for indexing. It concatenates a predefined prefix with the current encoded block number and returns it as a vector of bytes.

  2. Define the indexing data and save it using offchain indexing:

    use sp_io::offchain_index;
    const ONCHAIN_TX_KEY: &[u8] = b"my_pallet::indexing1";
    
    #[derive(Debug, Deserialize, Encode, Decode, Default)]
    struct IndexingData(Vec<u8>, u64);
    
    #[pallet::call]
    impl<T: Config> Pallet<T> {
    	#[pallet::weight(100)]
    	pub fn extrinsic(origin: OriginFor<T>, number: u64) -> DispatchResult {
    		let who = ensure_signed(origin)?;
    
    		let key = Self::derived_key(frame_system::Module::<T>::block_number());
    		let data = IndexingData(b"submit_number_unsigned".to_vec(), number);
    		offchain_index::set(&key, &data.encode());
    		Ok(())
    	}
    }
    
    impl<T: Config> Pallet<T> {
    	// -- skipped for brevity --
    }

    The indexing data can be any data type that can be bound by the Encode, Decode, and Deserialize traits. In the above code, data is stored via offchain indexing using the method.

  3. Use the offchain_worker hook method to read the data in the offchain workers' database:

    use sp_runtime::offchain::StorageValueRef;
    
    #[derive(Debug, Deserialize, Encode, Decode, Default)]
    struct IndexingData(Vec<u8>, u64);
    
    fn offchain_worker(block_number: T::BlockNumber) {
    	// Reading back the offchain indexing value. This is exactly the same as reading from
    	// ocw local storage.
    	let key = Self::derived_key(block_number);
    	let storage_ref = StorageValueRef::persistent(&key);
    
    	if let Ok(Some(data)) = storage_ref.get::<IndexingData>() {
    		debug::info!("local storage data: {:?}, {:?}",
    			str::from_utf8(&data.0).unwrap_or("error"), data.1);
    	} else {
    		debug::info!("Error reading from local storage.");
    	}
    
    	// -- snip --
    }

    With this, an offchain worker could read the corresponding data from a node's local storage. The how-to guide explains how to do that.

Related material

offchain_index::set()
Offchain local storage
Offchain operations
Offchain local storage