oracle

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2024 License: Apache-2.0 Imports: 20 Imported by: 0

README

Oracle Module

Abstract

The Oracle module provides the Ojo blockchain with an up-to-date and accurate price feed of exchange rates of multiple currencies against the USD.

As price information is extrinsic to the blockchain, the Ojo network relies on validators to periodically vote on current exchange rates, with the protocol tallying up the results once per VotePeriod and updating the on-chain exchange rates as the median of the ballot.

Since the Oracle service is powered by validators, you may find it interesting to look at the Staking module, which covers the logic for staking and validators.

Contents

  1. Concepts
  2. State
  3. End Block
  4. Messages
  5. Events
  6. Parameters

Concepts

Voting Procedure

During each VotePeriod, the Oracle module obtains consensus on the exchange rate of multiple denominations against USD specified in AcceptList by requiring all members of the validator set to submit a vote for exchange rates before the end of the interval.

Validators must first pre-commit to a set of exchange rates, then in the subsequent VotePeriod submit and reveal their exchange rates alongside a proof that they had pre-commited at those prices. This scheme forces the voter to commit to a submission before knowing the votes of others and thereby reduces centralization and free-rider risk in the Oracle.

  • Prevote and Vote

    Let P_t be the current time interval of duration defined by VotePeriod (currently set to 30 seconds, or 5 blocks) during which validators must submit two messages:

    • A MsgAggregateExchangeRatePrevote, containing the SHA256 hash of the exchange rates of multiple denominations. A prevote must be submitted for all different denominations specified in AcceptList.
    • A MsgAggregateExchangeRateVote, containing the salt used to create the hash for the aggregate prevote submitted in the previous interval P_t-1.
  • Vote Tally

    At the end of P_t, the submitted votes are tallied.

    The submitted salt of each vote is used to verify consistency with the prevote submitted by the validator in P_t-1. If the validator has not submitted a prevote, or the SHA256 resulting from the salt does not match the hash from the prevote, the vote is dropped.

    For each exchange rate the median of the votes is recorded on-chain as the effective rate for that denomination against USD for the following VotePeriod P_t+1.

    Exchange rates receiving fewer than VoteThreshold total voting power have their exchange rates deleted from the store.

  • Ballot Rewards

    After the votes are tallied, the winners of the ballots are determined with tally().

    Voters that have managed to vote within a narrow band around the median are rewarded with a portion of the collected seigniorage.

    The set of validators that can earn rewards is updated every SlashWindow by taking all the active validators at the start of a SlashWindow and updating the ValidatorRewardSet. Any validators that join in the middle of a SlashWindow will not be able to earn ballot rewards until the next SlashWindow.

    The reward portion is determined by the MissCounter amount for the voted on exchange rates accrued by each validator in the SlashWindow, where the validator with smallest MissCounter collects the most reward and the rest are rewarded logarithimically favoring fewer miss counts. See k.RewardBallotWinners() for more details.

Reward Band

Each asset has a unique RewardBand when it's being added to the Oracle Parameters. For some assets this needs to be smaller or larger in order to account for expected price flux / stability.

Let M be the median, 𝜎 be the standard deviation of the votes in the ballot, and R be the RewardBand for a given asset. The band around the median is set to be 𝜀 = max(𝜎, R/2). All valid (i.e. bonded and non-jailed) validators that submitted an exchange rate vote in the interval [M - 𝜀, M + 𝜀] should be included in the set of winners.

Reward Pool

The Oracle module's reward pool is composed of any tokens present in its module account. If there are no tokens present in the Oracle module reward pool during a reward period, no tokens are distributed for that period.

This reward pool will be filled initially by token inflation, then in a future upgrade will be funded by payment for the Ojo Validator services.

The reward pool is not distributed all at once, but instead over a period of time, determined by the param RewardDistributionWindow, currently set to 5256000.

Slashing

Be sure to read this section carefully as it concerns potential loss of funds.

A VotePeriod during which either of the following events occur is considered a "miss":

  • The validator fails to submits a vote for each and every exchange rate specified in AcceptList.

  • The validator fails to vote within the reward band around the median for one or more denominations.

During every SlashWindow, participating validators must maintain a valid vote rate of at least MinValidPerWindow (5%), lest they get their stake slashed (currently set to 0.01%). The slashed validator is automatically temporarily "jailed" by the protocol (to protect the funds of delegators), and the operator is expected to fix the discrepancy promptly to resume validator participation.

Abstaining from Voting

In Terra's implementation, validators have the option of abstaining from voting. To quote Terra's documentation :

A validator may abstain from voting by submitting a non-positive integer for the ExchangeRate field in MsgExchangeRateVote. Doing so will absolve them of any penalties for missing VotePeriods, but also disqualify them from receiving Oracle seigniorage rewards for faithful reporting.

In order to ensure that we have the most accurate exchange rates, we have removed this feature. Non-positive exchange rates in MsgAggregateExchangeRateVote are instead dropped.

The control flow for vote-tallying, exchange rate updates, ballot rewards and slashing happens at the end of every VotePeriod, and is found at the end-block ABCI function rather than inside message handlers.

State

ExchangeRate

An sdk.Dec that stores an exchange rate against USD.

  • ExchangeRate: 0x01 | byte(denom) -> sdk.Dec
FeederDelegation

An sdk.AccAddress (ojo- account) address for operator price feeder rewards.

  • FeederDelegation: 0x02 | byte(valAddress length) | byte(valAddress) -> sdk.AccAddress
MissCounter

An int64 representing the number of VotePeriods that validator operator missed during the current SlashWindow.

  • MissCounter: 0x03 | byte(valAddress length) | byte(valAddress) -> ProtocolBuffer(uint64)
AggregateExchangeRatePrevote

AggregateExchangeRatePrevote containing a validator's aggregated prevote for all denoms for the current VotePeriod.

  • AggregateExchangeRatePrevote: 0x04 | byte(valAddress length) | byte(valAddress) -> ProtocolBuffer(AggregateExchangeRatePrevote)
// AggregateVoteHash is a hash value to hide vote exchange rates
// which is formatted as hex string in SHA256("{salt}:{exchange rate}{denom},...,{exchange rate}{denom}:{voter}")
type AggregateVoteHash []byte

type AggregateExchangeRatePrevote struct {
    Hash        AggregateVoteHash // Vote hex hash to keep validators from free-riding
    Voter       sdk.ValAddress    // Voter val address
    SubmitBlock int64
}
AggregateExchangeRateVote

AggregateExchangeRateVote containing a validator's aggregate vote for all denoms for the current VotePeriod.

  • AggregateExchangeRateVote: 0x05 | byte(valAddress length) | byte(valAddress) -> ProtocolBuffer(AggregateExchangeRateVote)
type DecCoin struct {
    Denom     string  `json:"denom"`
    Amount    sdk.Dec `json:"amount"`
}

type DecCoins []DecCoin

type AggregateExchangeRateVote struct {
    ExchangeRates  sdk.DecCoins    // ExchangeRates against USD
    Voter          sdk.ValAddress  // voter val address of validator
}

End Block

Tally Exchange Rate Votes

At the end of every block, the Oracle module checks whether it's the last block of the VotePeriod. If it is, it runs the Voting Procedure:

  1. All current active exchange rates are purged from the store

  2. Received votes are organized into ballots by denomination. Votes by inactive or jailed validators are ignored.

  3. Exchange rates not meeting the following requirements will be dropped:

    • Must appear in the permitted denominations in AcceptList
    • Ballot for rate must have at least VoteThreshold total vote power
  4. For each remaining denom with a passing ballot:

    • Tally up votes and find the median exchange rate and winners with tally()
    • Iterate through winners of the ballot and add their weight to their running total
    • Set the exchange rate on the blockchain for that denom with k.SetExchangeRate()
    • Emit an exchange_rate_update event
  5. Count up the validators who missed the Oracle vote and increase the appropriate miss counters

  6. If at the end of a SlashWindow, penalize validators who have missed more than the penalty threshold (submitted fewer valid votes than MinValidPerWindow)

  7. Distribute rewards to ballot winners with k.RewardBallotWinners()

  8. Clear all prevotes (except ones for the next VotePeriod) and votes from the store

Messages

See oracle tx proto for list of supported messages.

Events

See oracle events proto for list of supported events.

Params

See oracle events proto for list of module parameters.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CalcPrices added in v0.1.2

func CalcPrices(ctx sdk.Context, params types.Params, k keeper.Keeper) error

func EndBlocker

func EndBlocker(ctx sdk.Context, k keeper.Keeper) error

EndBlocker is called at the end of every block

func ExportGenesis

func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState

ExportGenesis returns the x/oracle module's exported genesis.

func InitGenesis

func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, genState types.GenesisState)

InitGenesis initializes the x/oracle module's state from a provided genesis state.

func Tally

func Tally(
	ballot types.ExchangeRateBallot,
	rewardBand sdk.Dec,
	validatorClaimMap map[string]types.Claim,
	incrementWin bool,
) (sdk.Dec, error)

Tally calculates and returns the median. It sets the set of voters to be rewarded, i.e. voted within a reasonable spread from the weighted median to the store. Note, the ballot is sorted by ExchangeRate.

Types

type AppModule

type AppModule struct {
	AppModuleBasic
	// contains filtered or unexported fields
}

AppModule implements the AppModule interface for the x/oracle module.

func NewAppModule

func NewAppModule(
	cdc codec.Codec,
	keeper keeper.Keeper,
	accountKeeper types.AccountKeeper,
	bankKeeper bankkeeper.Keeper,
) AppModule

func (AppModule) BeginBlock

func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)

BeginBlock executes all ABCI BeginBlock logic respective to the x/oracle module.

func (AppModule) EndBlock

EndBlock executes all ABCI EndBlock logic respective to the x/oracle module. It returns no validator updates.

func (AppModule) ExportGenesis

func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage

ExportGenesis returns the x/oracle module's exported genesis state as raw JSON bytes.

func (AppModule) GenerateGenesisState

func (AppModule) GenerateGenesisState(simState *module.SimulationState)

GenerateGenesisState creates a randomized GenState of the distribution module.

func (AppModule) InitGenesis

func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate

InitGenesis performs the x/oracle module's genesis initialization. It returns no validator updates.

func (AppModule) Name

func (am AppModule) Name() string

Name returns the x/oracle module's name.

func (AppModule) ProposalContents

func (am AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg

ProposalContents returns all the oracle content functions used to simulate governance proposals.

func (AppModule) QuerierRoute

func (AppModule) QuerierRoute() string

QuerierRoute returns the x/oracle module's query routing key.

func (AppModule) RegisterInvariants

func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)

RegisterInvariants registers the x/oracle module's invariants.

func (AppModule) RegisterServices

func (am AppModule) RegisterServices(cfg module.Configurator)

RegisterServices registers gRPC services.

func (AppModule) RegisterStoreDecoder

func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry)

RegisterStoreDecoder registers a decoder for oracle module's types

func (AppModule) WeightedOperations

func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation

WeightedOperations returns the all the oracle module operations with their respective weights.

type AppModuleBasic

type AppModuleBasic struct {
	// contains filtered or unexported fields
}

AppModuleBasic implements the AppModuleBasic interface for the x/oracle module.

func NewAppModuleBasic

func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic

func (AppModuleBasic) ConsensusVersion

func (AppModuleBasic) ConsensusVersion() uint64

func (AppModuleBasic) DefaultGenesis

func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage

DefaultGenesis returns the x/oracle module's default genesis state.

func (AppModuleBasic) GetQueryCmd

func (AppModuleBasic) GetQueryCmd() *cobra.Command

GetQueryCmd returns the x/oracle module's root query command.

func (AppModuleBasic) GetTxCmd

func (AppModuleBasic) GetTxCmd() *cobra.Command

GetTxCmd returns the x/oracle module's root tx command.

func (AppModuleBasic) Name

func (AppModuleBasic) Name() string

Name returns the x/oracle module's name.

func (AppModuleBasic) RegisterGRPCGatewayRoutes

func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)

RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the x/oracle module.

func (AppModuleBasic) RegisterInterfaces

func (AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry)

RegisterInterfaces registers the x/oracle module's interface types.

func (AppModuleBasic) RegisterLegacyAminoCodec

func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)

RegisterLegacyAminoCodec registers the x/oracle module's types with a legacy Amino codec.

func (AppModuleBasic) RegisterRESTRoutes deprecated

func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router)

Deprecated: RegisterRESTRoutes performs a no-op. Querying is delegated to the gRPC service.

func (AppModuleBasic) ValidateGenesis

func (AppModuleBasic) ValidateGenesis(
	cdc codec.JSONCodec,
	_ client.TxEncodingConfig,
	bz json.RawMessage,
) error

ValidateGenesis performs genesis state validation for the x/oracle module.

Directories

Path Synopsis
client
cli
Package types is a reverse proxy.
Package types is a reverse proxy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL