LogoLogo
  • Imua
    • About
  • Manifesto
    • The Problems
    • The Principles
  • Architecture
    • Imua Design Principles
    • Imua Network
    • Imua Modules
    • Client Chain Bridges
      • Trustless Verification of Client Chain State
      • Handling Race Conditions between Imua and Client Chains
    • Client Chain Contracts
  • Concepts
    • Ecosystem
      • Re/stakers
      • Operators
      • Services (AVS)
        • Service Integration with Imua
        • Service Committee
        • Service Integration Details
    • restaked Proof-of-Stake (rPOS)
    • Multi-Token Restaking
    • Multi-Chain Restaking with Trustless Bridging
    • Voting Power
    • Price Oracle
    • Flexible Integration with AVS
    • Tribe Staking
  • Governance
  • Risk Management
    • Risk Analysis
      • Risk Modeling
      • Risk Parameters
      • Crypto-Economic Risk
      • Unintended Slashing
      • Black Swan Events
    • Risk Mitigation
      • Smart Contract Simplicity
      • Audits
      • Slashing Prevention
      • Slashing Vetos
      • Insurance Pools
      • Circuit Breakers
  • Components
    • Testnet
    • Oracle Module
      • Reaching Consensus on Asset Prices
      • Penalty
      • Implementation Detail
    • Smart Contracts
    • Explorer
    • Registry
  • Validator Setup
    • Prerequisites
    • Node Install
    • Compiling Binary from Source
    • Oracle Price Feeder
    • Running the Node
    • Snapshot
    • Register Option 1 (Bootstrap)
    • Register Option 2 (Post Network Launch)
    • Deposit Tokens
    • Delegating Tokens
    • Confirm Election Status
    • Faucets
    • Managing The Validator
    • Security Best Practices
    • Risks & Mitigation
    • Participation in Governance
    • FAQs & Resources
  • Testnet Upgrade to v1.1.2
  • AVS Setup
    • AVS Overview
    • Prerequisites
    • Enhanced and Automated Edition of hello-avs integration guide&example
    • Building the AVS in Imua
    • Hello-World-AVS Example
    • Becoming AVS Operator
    • AVS Register and Deploy
    • AVS Task Example
  • Whitepaper (2023)
    • .pdf
  • FAQ
    • What problems is Imua solving?
    • What are the main design trade-offs that had to be made with an omnichain design?
    • Does the omnichain design imply added trust assumptions (relative to a single-chain design)?
    • What concurrency-related challenges would you face with a different design?
    • How does Imua integrate with new chains?
    • Do specific chains prove unique challenges w.r.t. integration?
    • How is the cross-chain communication is achieved?
    • What are the known attack / censorship vectors here, if any?
    • Are the restaked tokens being pooled in a centralized account?
    • Who will run the validators in the Imua network?
    • Is Imua an AVS?
    • How does Imua address the risks of overloading L1 social consensus?
    • Does the Imua queuing system raise concerns around latency?
    • What are the main benefits of an omnichain design?
Powered by GitBook
On this page
  • Workflow
  • Message
  • TokenFeeder
  1. Components
  2. Oracle Module

Implementation Detail

PreviousPenaltyNextSmart Contracts

Last updated 3 months ago

Workflow

  1. Validator Price Retrieval:

    The validator obtains the price information from the chosen price source (for version 1, only Chainlink is accepted as the price source).

  2. Price Feed Transaction:

    • The validator packages the obtained price into a transaction and sends it to Imuachain

    • Since the price-feed transaction provides support at the consensus layer, it maintains consistency with Tendermint by using the consensus key and adopting Ed25519 signing.

  3. AnteHandler:

    Transactions are only broadcasted once they pass the following validation checks:

    • The validator must be valid and part of the active validator set.

    • The round status should be valid—meaning the asset pair in the price information falls within the open quoting window for that round.

    • Nonce validation:

      • The nonce must be between 1 and maxNonce.

      • If the nonce is greater than 1, a transaction with nonce n-1 must have already been successfully submitted.

      • No duplicate nonce values are allowed.

  4. Filter:

    After a transaction passes initial validation, it undergoes further steps:

    1. The transaction messages are cached to enable replay and restore data in case of node restarts.

      • The system checks the performance of all active validators at the end of the quoting window. If any underperforming validators are identified, they will be penalized through slashing or jailing.

    2. The calculator is called to compute the consensus price based on the received deterministic prices (e.g., from Chainlink).

      • A 'deterministic price' refers to a price obtained from a source like Chainlink, where each price is associated with a unique ID. For each specific ID, the price is clear and unambiguous, ensuring no ambiguity in its value.

      • In the oracle module context, the unique ID for a deterministic price obtained from Chainlink is referred to as DetID

  5. Calculator:

    If multiple validators submit different prices for the same DetID from the same source, the calculator accumulates voting power for each price and selects the price that surpasses the threshold as the consensus price.

    In Imuachain testnet, the threshold is defined as 2/3 of the total voting power.

  6. Aggregator:

    The aggregator is responsible for computing the final consensus price after receiving the quotes from validators:

    1. For each validator:

      The aggregator calculates the final price for each deterministic source like Chainlink by considering the consensus price computed by the calculator.

      • For version 1 (V1 of the oracle module), since there is only a single price source (Chainlink), the final price for each validator is simply the consensus price for that source.

    2. The final consensus price for the round is computed by aggregating the final prices from all validators. The aggregation can be done using different methods (e.g., median, average). Currently, we use the median for aggregation, but since there is only a single deterministic source, this effectively means taking the single price.

      Example:

      • Current validator set: {val_1: 1, val_2: 1, val_3: 1, val_4: 1}

      • Validators' quotes:

        • val_1: [{source1: {price: 1_1, detID: 1}}, {source2: {price: 1_2}}]

        • val_2: [{source1: {price: 1_2, detID: 1}}, {source2: {price: 2_2}}]

        • val_3: [{source1: {price: 1_1, detID: 1}}, {source2: {price: 3_3}}]

        • val_4: [{source1: {price: 1_1, detID: 1}}, {source2: {price: 4_4}}]

      • The calculator identifies the consensus price for source1 as {price: 1_1, detID: 1}, so val_2's quote for source1&detID_1 is considered incorrect.

      • The calculator updates val_2's quote to the consensus price {price: 1_1, detID: 1}.

      • Aggregator computes the validator’s final price by averaging across that validator's prices from different sources(currently we only have chainlink as the single source)

        • For example, val_1: [{source1: {price: 1_1, detID: 1}}, {source2: {price: 1_2}}] → val_final_price = (1_1 + 1_2) / 2.

      • ~~The final consensus price for the round is aggregated from all validator final prices by selecting the price that has accumulated enough voting power to exceed the threshold.~~The final consensus price for the round is calculated by aggregating all validators' final prices using a statistical method, such as a weighted median or average. In V1 of the oracle module, since Chainlink is the only price source, the aggregation simplifies to taking the median of all validators' final prices, which effectively selects the price that best represents the consensus among all submitted quotes.

    The default number of the threshold is defined as 2/3

Message

   type MsgCreatePrice struct {
       // creator tells which is the message sender and should sign this message
       Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"`
       //refer to id from Params.TokenFeeders, 0 is reserved, invalid to use
       FeederID uint64 `protobuf:"varint,2,opt,name=feeder_id,json=feederId,proto3" json:"feeder_id,omitempty"`
       // prices price with its corresponding source
       Prices []*PriceSource `protobuf:"bytes,3,rep,name=prices,proto3" json:"prices,omitempty"`
       //on which block commit does this message be built on
       BasedBlock uint64 `protobuf:"varint,4,opt,name=based_block,json=basedBlock,proto3" json:"based_block,omitempty"`
       // nonce represents the unique number to disginguish duplicated messages
       Nonce int32 `protobuf:"varint,5,opt,name=nonce,proto3" json:"nonce,omitempty"`
   }

The price quote transaction is of type create-price transaction.

  • Creator: Transaction sender

    The ~~create-price~~ price-feed transaction needs to be signed by the validator's consensus key, so the creator corresponds to the base64 representation of the virtual AccAddress generated by the validatorConsKey

  • FeederID

    Corresponds to an tokenFeeder which describes the quoting status of an asset pair. Each tokenFeeder corresponds to a specific token, and each token can have only one active tokenFeeder at any time.

  • BasedBlock

    It represents the block before the first block of the current quoting window.

    • e.g., in the previous example, round_1's quote window is {100, 101, 102}, so basedBlock =99 (100-1)

  • Nonce

    To ensure system stability and avoid duplicate price-feed transactions, we limit the number of quote transactions each active validator can submit during each round. The maximum number of transactions allowed is equal to the quoting window size.

    • For example, if the quoting window is of size 3, the valid Nonce values are 1, 2, and 3. Validators can only submit one transaction with each nonce in the quoting window.

    • The nonce follows the same principles as regular transaction nonces, incrementing from 1:

      • If a transaction with nonce=1 is not submitted, submitting a nonce=2 transaction directly will be rejected.

      • Transactions with duplicate nonces will be rejected.

    This setup ensures that validators submit their quotes in an orderly and predictable manner within each quoting window.

  • Prices Prices []*PriceSource

       type PriceSource struct {
           // source_id refers to id from Params.SourceList, where this price fetched from, 0 is reserved for custom usage
           SourceID uint64 `protobuf:"varint,1,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"`
           // if source is deteministic like chainlink with roundID, set this value with which returned from source
           // up to 3 values in case of the async of network, to give more time for oracle nodes(validators) get into consensus
           // eg.with deterministic source, this array will contian 3 continuous values up to latest
           // for non-deterministic source, it's a choice by v2 rules.
           Prices []*PriceTimeDetID `protobuf:"bytes,2,rep,name=prices,proto3" json:"prices,omitempty"`
           // used for 0-sourceID-customDefinedSource
           Desc string `protobuf:"bytes,3,opt,name=desc,proto3" json:"desc,omitempty"`
       }
    • Each PriceSource describes prices from a "source." In V1 of the oracle module, we restrict valid "sources" to Chainlink, which is represented by SourceID = 1.

    • Prices Prices []*PriceTimeDetID

         type PriceTimeDetID struct {
             // price at a specific point(timestamp of non-deterministic source, roundId of deteministic source)
             Price string `protobuf:"bytes,1,opt,name=price,proto3" json:"price,omitempty"`
             // decimal of the corresponding price
             Decimal int32 `protobuf:"varint,2,opt,name=decimal,proto3" json:"decimal,omitempty"`
             // timestamp when the price corresponding to
             Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
             // det_id is used for deterministic source to tell of which round from this source the price is corresponded
             DetID string `protobuf:"bytes,4,opt,name=det_id,json=detId,proto3" json:"det_id,omitempty"`
         }

      describes the price information provided by the 'source':

      • Price: Integer representation of the price.

      • Decimal: The number of decimal places for the price.

        • Example: {Price: 1234567, Decimal: 2} means the price is 12345.67.

      • Timestamp: The timestamp corresponding to the price.

        • For sources with deterministic IDs (like Chainlink's roundID), the timestamp is not validated by the exocored system, and the ID takes precedence.

      • DetID: The ID of the deterministic source. For Chainlink, this corresponds to the chainlink_roundID for the provided price.

TokenFeeder

A tokenFeeder represents the configuration and management of the price quoting process for a specific token.

type TokenFeeder struct {
        // refer to params.tokenList, from 1
        TokenID uint64 `protobuf:"varint,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"`
        // refer to params.ruleList, 0 means no restriction, accept any source including customer defined
        RuleID uint64 `protobuf:"varint,2,opt,name=rule_id,json=ruleId,proto3" json:"rule_id,omitempty"`
        // include, from 1, when some token's feeder had been stop and then restart,
        // the token_id will be continuous from previous one
        StartRoundID uint64 `protobuf:"varint,3,opt,name=start_round_id,json=startRoundId,proto3" json:"start_round_id,omitempty"`
        // include, first block which start_round_id can be settled is at least start_base_block+1
        StartBaseBlock uint64 `protobuf:"varint,4,opt,name=start_base_block,json=startBaseBlock,proto3" json:"start_base_block,omitempty"`
        // set as count of blocks, for how many blocks interval the price will be update once
        Interval uint64 `protobuf:"varint,5,opt,name=interval,proto3" json:"interval,omitempty"`
        // tokenfeeder is initialized with forever live, update the End parameters by voting,
        // and will off service by the end
        // this is set by updateParams, and the EndRoundID will be update by related. excluded,
        // will not work if current height >=EndBlock
        EndBlock uint64 `protobuf:"varint,6,opt,name=end_block,json=endBlock,proto3" json:"end_block,omitempty"`
}
  • TokenID:

    The asset this TokenFeeder corresponds to, identified by its index in the oracle params' asset list.

  • RuleID:

    Specifies how price sources should be verified. In v1 of the oracle module, this ensures price data corresponds to Chainlink.

  • StartRoundID:

    The first round ID where the TokenFeeder begins. Round IDs increment monotonically and are used to synchronize tokenFeeders for the same token.

    • Only one active TokenFeeder per token is allowed at any time, defined by the StartBaseBlock and EndBlock.

    • To continue price feeds for a token after a TokenFeeder ends, the next TokenFeeder will begin at the last round ID of the previous one plus 1.

  • StartBaseBlock:

    The block height after which the TokenFeeder starts, marking the earliest block after which price-feed transactions for this token can be accepted.

  • Interval:

    The number of blocks per round; round IDs increment by 1 every interval blocks.

  • EndBlock:

    The block height when the TokenFeeder stops. A value of 0 means it does not stop, and it can be updated through parameters.

When validators submit price-feed transactions, they interact with the TokenFeeder, not the token itself.