Oracle Module
Overview
Oracle is a Cosmos SDK-based project offering a unique Oracle module for secure interactions with external data sources. It employs a data-agnostic voting process that encourages validator engagement. The primary objective of this module is to supply dependable, up-to-date information from external sources to the chain, which is crucial for network state updates and other actions.
The Oracle module is explicitly designed for seamless integration into any Cosmos-based chain, hereafter referred to as "the chain". It achieves this through a callback interface that allows other modules to implement and register onto the "oracle callback registry". This registration enables participation in the voting process and the use of majority vote data for state updates.
The Oracle Feeder is tasked with periodically submitting votes on the observed external data on behalf of the chain's validators. Before voting, validators need to delegate their consent to feeders using MsgDelegateFeedConsent.
Concepts
Oracle Vote
Feeders supplying external data should classify the data (payloads) based on the consuming module and the data source (namespace) from where it is retrieved.
Each vote from a feeder has the following structure:
message OracleVote {
string validator = 1;
repeated ModuleVote module_votes = 2;
}
message ModuleVote {
string module = 1;
repeated NamespaceVote namespace_votes = 2;
}
message NamespaceVote {
string namespace = 1;
string payload = 2; // contains the external data for a specific module-namespace pair
}
validator
The address of the validator submitting the vote.
module_votes
A list of ModuleVote
, submitted by a MsgOracleVote
message. Here's an example:
[
{
"module": "module-a",
"namespace_votes": {
{
"namespace": "namespace-a",
"payload": "module-a-namespace-a-payload"
}
}
},
{
"module": "module-b",
"namespace_votes": {
{
"namespace": "namespace-x",
"payload": "module-b-namespace-x-payload"
},
{
"namespace": "namespace-y",
"payload": "module-b-namespace-y-payload"
}
}
}
]
namespace_votes
A list of NamespaceVote
.
payload
The payload
field holds the external data agreed upon by the majority of validators.
Oracle Vote Callback
Chain modules can implement and register the following callback on the oracle callback registry to utilize the oracle data:
type OracleVoteCallback interface {
ComputePayloadHash(ctx sdk.Context, voteInfo VoteInfo) ([]byte, error)
OnMajorityVote(ctx sdk.Context, voteInfo VoteInfo) error
}
Module developers can register an implementation of this interface into the oracle callback registry in the following
manner within the app.go
file:
app.OracleKeeper.RegisterVoteCallback(aModuleName, OracleVoteCallbackImpl())
The oracle, indifferent to payload content, requires the target module to compute a hash for the payload using
the ComputePayloadHash
callback method. It then announces the most frequently repeated hash along with its
corresponding payload as the majority vote, formatted as follows:
type VoteInfo struct {
Namespace string
Payload string
LastVotingPeriodTime time.Time
}
The prepared VoteInfo
is sent to its target module via the OnMajorityVote
callback method, provided the majority
vote is a Valid Vote.
Voting Procedure
For external data to be incorporated into the chain, two VotePeriod
s are required. The first vote period involves
submission of a pre-vote message with an encrypted version of the data. During the second vote period, a vote message is
submitted, revealing the actual data. Once the second vote period concludes, vote processing commences, and the majority
vote is forwarded to interested modules.
Pre-vote and Vote
Validators need to submit two messages during each vote period:
- A
MsgOraclePreVote
, containing a SHA256hash
calculated by the feeder from themodule_votes
, a randomsalt
, and thevalidator
address, in the formatsha256(salt:module_votes:validator)
. Pre-votes can be overridden by submitting another pre-vote within the same vote period. - A
MsgOracleVote
, containingvalidator
,module_votes
, andsalt
used to generate the hash for the pre-vote in the previous vote period. This message discloses themodule_votes
and enables vote processing to start at the onset of the next vote period. On receipt of this message, the oracle module computes a hash using themodule_votes
,salt
, and the validator to authenticate that the validator pre-voted for the samemodule_votes
. If the computed hash does not align with the pre-vote hash, the vote is discarded. This commitment scheme mitigates the risk of centralization and free-riding in the oracle module, as it incentivizes validators to submit honest, timely votes.
Vote Processing
Categorize
Vote processing commences with categorizing all oracle votes. For a specific <module,namespace>
pair, a ballot
of Vote
s each with the following structure is created:
type Vote struct {
Type VoteType
Validator string
Power int64
Payload string
PayloadHash string
}
The Vote
struct tracks essential details about each vote:
Type
: aVoteType
enum indicating the vote type, which can beValidVote
,NoVote
, orAbstainVote
.Validator
: a string containing the address of the voting validator.Power
: an int64 representing the voting power of the validator. The voting power is determined by the quantity of bonded tokens held by the validator.Payload
: a string containing the vote's associated payload.PayloadHash
: a string containing the hash of the payload linked to the vote.
type Ballot map[string]*Vote
A ballot is a map linking a validator to its corresponding Vote
for a specific <module,namespace>
pair. The oracle
module applies a tallying process to each ballot to identify the majority vote, with the assistance of the target
module.
Types of Votes
Three types of votes can be defined for a given <module,namespace>
pair:
-
No Vote
This occurs when a feeder doesn't submit a vote for the pair.
💡 If a feeder fails to submit an
OracleVote
, it's equivalent to casting a No Vote for all<module,namespace>
pairs. -
Abstain Vote
This occurs when a feeder submits a vote with an empty payload for the pair. If the payload's hash can't be computed, i.e.,
ComputePayloadHash
returns an error, the vote is considered an Abstain Vote.💡 Validators can abstain from voting, which exempts them from penalties for missed
VotePeriod
s but also excludes them from receiving oracle rewards for accurate reporting. -
Valid Vote
This occurs when a feeder submits a vote with a non-empty payload for the pair, and the payload's hash is successfully computed using
ComputePayloadHash
.
Tally Process
A mapping from validators to their corresponding Claim
(referred to as validatorClaimMap
) is defined to track
validators' behavior. This tracking is later used to apply slash/reward strategies. The Claim
structure is as follows:
type Claim struct {
RewardCount int64 // Number of times a validator voted for the majority vote
MissCount int64 // Number of times validator misbehaved, e.g., missed voting for payload or voted for a non-majority payload
MaxMissRateThresholdReached bool // Indicates whether the MaxMissRateThreshold has been reached
Voted bool // Indicates whether a vote has been submitted for the validator
}
The validatorClaimMap
is updated during the tally process, which is applied per <module,namespace>
pair. The tally
process results in one of two outcomes:
-
Quorum is not reached
If the sum of the powers for all votes submitted for this pair doesn't reach the quorum threshold, no updates are made to the
validatorClaimMap
.💡 If most validators submit an Abstain Vote for a pair and others don't, it suggests most validators concur that a specific
<module,namespace>
pair exists on the chain. Validators who haven't voted for this pair should be penalized. However, for simplicity, validators who skipped voting for this pair aren't penalized since the quorum isn' t reached. -
Quorum is reached
If the sum of voting power for the most-voted payload reaches the
VoteThreshold
parameter, the vote is declared a majority. If it falls short, the vote isn't considered a majority.-
Majority is achieved
-
If the majority vote is No Vote:
The majority of feeders concur that a specific
<module,namespace>
pair doesn't exist. As such, feeders who voted for this pair, i.e., the minority who submitted a Valid Vote, should have theirMissCount
s incremented.RewardCount
s remain unchanged for all validators.💡 The oracle, being privy to all registered modules, will reject any oracle votes that include a
ModuleVote
for an unregistered module. -
In the event of a Valid Vote majority:
The majority of feeders agree on a specific
<module,namespace>
pair and payload, i.e., the majority payload. Consequently, validators either not voting or not aligning with the majority vote will see theirMissCount
s increased. Additionally,RewardCount
increases for each validator voting with the majority.
💡 Note that in both situations above, no update is made to the validator’s
Claim
if they submitted an Abstain Vote. -
-
Majority is not achieved:
In this case, no updates are made to the
validatorClaimMap
.
-
Slashing Validators
Validators exceeding the acceptable threshold of misbehavior ( i.e., MaxMissRatePerSlashWindow) are slashed and jailed following each SlashWindow via a call to the slashAndResetMissCounters function.
Misbehaviors are tracked via a MissCounter object for each validator. The counter increases
when claim.MissCount
surpasses a threshold calculated as follows:
Here, represents the total number of majority achievements for all <module,namespace>
pairs during the current vote process.
Rewarding Validators
A segment of the fees collected in the feeCollector
account serves as the source for reward distribution. The
portion size is determined by the FeeCollectorRewardRatio
parameter, as detailed in
the FeeCollectorRewardRatio documentation. This amount is transferred into the
Oracle module account during the oracle BeginBlocker
function.
Rewards are allocated to validators at the conclusion of each VotePeriod
, after vote processing. The
reward for each validator is calculated as follows:
Where
And equals the balance of the oracle module account.
Transitions
Begin Block
At the start of each block, some funds are moved from the feeCollector
account to the oracle
module
account, based on the FeeCollectorRewardRatio
parameter. This action encourages validators to supply accurate and
timely data by offering a portion of the collected fees. The following pseudo-code outlines the process:
if k.GetParams(ctx).FeeCollectorRewardRatio.IsZero() {
return
}
// Move a portion of fees from the feeCollector account to the oracle module account
feeCollectorAddress := k.accountKeeper.GetModuleAddress(k.feeCollectorName)
balances := k.bankKeeper.SpendableCoins(ctx, feeCollectorAddress)
rewards := k.calculateTotalRewards(ctx, balances)
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, rewards); err != nil {
panic(err)
}
End Block
At each block's end, two primary tasks are performed: finalizing votes and distributing rewards at voting periods' conclusion, and slashing validators at the end of slash windows.
When a voting period ends, the function processes the votes, calculates winners' rewards, distributes the rewards, and erases processed votes. An event is emitted to signal the end of the voting period.
When a slash window ends, the function penalizes validators who failed to meet the voting threshold and resets their miss counters.
Here's a pseudocode of the whole process:
if util.IsPeriodLastBlock(ctx, params.VotePeriod) {
// Update voting period time for the next period
k.setCurrentVotingPeriodTime(ctx, ctx.BlockTime())
// Obtain the last voting period time. The reported exchange rates are expected to be the latest rate before this time
lastVotingPeriodTime, found := k.GetLastVotingPeriodTime(ctx)
// Not found implies this is the first voting period, hence we skip voting
if found {
var validatorClaimMap map[string]types.Claim
validatorClaimMap, ballotVoteResults = k.processPeriodVotes(ctx, lastVotingPeriodTime, validatorPowers)
// Distribute voting rewards to validators
validatorRewards := k.rewardBallotWinners(ctx, validatorClaimMap, validatorPowers)
validatorSummaries = k.getValidatorSummaries(ctx, validatorPowers, validatorClaimMap, validatorRewards)
// Clear processed votes
k.clearPeriodVotes(ctx)
} else {
validatorSummaries = k.getValidatorSummaries(ctx, validatorPowers, map[string]types.Claim{}, map[string]sdk.Coins{})
}
// Emit vote period end event
err := ctx.EventManager().EmitTypedEvent(&types.EventVoteIntervalEnds{
TimeMillis: ctx.BlockTime().UnixMilli(),
PreviousVoteIntervalEndTimeMillis: lastVotingPeriodTime.UnixMilli(),
BlockHeight: ctx.BlockHeight(),
VotePeriod: params.VotePeriod,
ValidatorSummaries: validatorSummaries,
BallotVoteResults: ballotVoteResults,
})
if err != nil {
panic(err)
}
}
// Slash validators who missed the voting threshold and
// reset all validators' miss counters at the last block of slash window
if util.IsPeriodLastBlock(ctx, params.SlashWindow) {
k.slashAndResetMissCounters(ctx)
}
Functions
GetVoteHash
func GetVoteHash(salt string, moduleVotes string, voter string) []byte
This function calculates the truncated SHA256 hash value from salt:moduleVotes:voter
for a vote, which is submitted in
a MsgOraclePreVote
in the preceding VotePeriod
.
categorizeBallots
The categorizeBallots
function sorts the provided oracle votes into ballots based on the <module,namespace>
pair.
tally
func (k Keeper) tally(
ballot types.Ballot,
validatorClaimMap map[string]types.Claim,
majorityThreshold sdk.Dec
) (majorityVote *types.Vote, majorityAchieved bool)
The tally
function tallies the votes for a particular ballot and determines the majority vote. It also updates the
claims for each validator.
slashAndResetMissCounters
func (k Keeper) slashAndResetMissCounters(ctx sdk.Context)
The slashAndResetMissCounters
function is invoked at the conclusion of every SlashWindow
. It examines the miss
counters of all validators to see if the MissCounter
exceeds a certain threshold, calculated as follows:
If the MissCounter
exceeds the threshold, the validator is penalized with a slash-fraction and is jailed. After all
validators have been checked, all MissCounter
s are reset to zero for the next SlashWindow
.
Messages
MsgUpdateParams
The MsgUpdateParams
message allows for the updating of the oracle module's parameters. Please note that this message
is only accessible via the gov module, and cannot be used directly.
message MsgUpdateParams {
string authority = 1;
Params params = 2;
}
The structure and definitions of the oracle module parameters are outlined in the Parameters section.
MsgOraclePreVote
The MsgOraclePreVote
message enables a pre-vote for an oracle vote.
message MsgOraclePreVote {
string feeder = 1;
string hash = 2;
string validator = 3;
}
The hash
is a hex string derived from the leading 20 bytes of the SHA256 hash (hex string) of a string formatted
as salt:namespace_votes:validator
. This string is the metadata of the actual MsgOracleVote
that follows in the
next VotePeriod
.
The GetVoteHash function can be used to encode this hash. Note that since the salt must be
revealed in the subsequent MsgOracleVote
, a new salt must be generated for each pre-vote submission.
The feeder
account address (AccAddress
) is used if the validator wants to delegate vote signing to a separate key (
the "feeder") to avoid exposing their validator signing key.
The validator
is the original validator's address (ValAddress
).
MsgOracleVote
The MsgOracleVote
message facilitates the submission of an oracle vote.
message MsgOracleVote {
string feeder = 1;
string validator = 2;
string salt = 3;
string module_votes = 4;
}
The salt
parameter must match the salt used to create the pre-vote. If it doesn't, the vote will not be
accepted. module_votes
is a string derived from converting the ModuleVotes
type to JSON.
MsgOracleCombinedVote
To simplify the process for validators providing oracle data and maximize block space, the oracle module provides
a MsgOracleCombinedVote
. This enables a vote for the current period and a pre-vote for the subsequent period. It
ensures validators don't have to sign multiple transactions to provide oracle data for a vote period.
message MsgOracleCombinedVote {
string feeder = 1;
string validator = 2;
string pre_vote_hash = 3;
string vote_salt = 4;
string vote_module_votes = 5;
}
pre_vote_hash
, feeder
, and validator
are described in MsgOraclePreVote. vote_salt
and vote_module_votes
are detailed in MsgOracleVote.
MsgDelegateFeedConsent
Validators can delegate voting rights to another key to avoid keeping the block signing key online. They submit
a MsgDelegateFeedConsent
, delegating their voting rights to a delegate
that
signs MsgOraclePreVote
, MsgOracleVote
, and/or MsgOracleCombinedVote
on their behalf.
💡 Note: Delegates will likely require a deposit to cover fees, sent in a separate MsgSend
. This off-chain agreement
isn't enforced.
message MsgDelegateFeedConsent {
string operator = 1;
string delegate = 2;
}
The operator
field contains the validator's operator address (ValAddress
). The delegate
field is the account
address (AccAddress
) of the delegate account, which will submit votes and pre-votes on behalf of the operator
.
Query
Params
This query fetches the current value of module parameters.
message QueryParamsRequest {}
message QueryParamsResponse {
Params params = 1;
}
The command line interface (CLI) supports the following command:
oracled query oracle params
Oracle Pre-vote
This query fetches the latest oracle pre-vote for a validator.
message QueryGetOraclePreVoteRequest {
string validator = 1;
}
message QueryGetOraclePreVoteResponse {
OraclePreVote oracle_pre_vote = 1;
}
The CLI supports the following command:
oracled query oracle show-oracle-pre-vote [validator]
Oracle Pre-votes
This query fetches a list of pre-votes for all validators.
message QueryAllOraclePreVoteRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}
message QueryAllOraclePreVoteResponse {
repeated OraclePreVote oracle_pre_vote = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
The CLI supports the following command:
oracled query oracle list-oracle-pre-vote
Oracle Vote
This query fetches the latest oracle vote for a validator.
message QueryGetOracleVoteRequest {
string validator = 1;
}
message QueryGetOracleVoteResponse {
OracleVote oracle_vote = 1;
}
The CLI supports the following command:
oracled query oracle show-oracle-vote [validator]
Oracle Votes
This query fetches a list of votes for all validators.
message QueryAllOracleVoteRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}
message QueryAllOracleVoteResponse {
repeated OracleVote oracle_vote = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
Use the following CLI command:
oracled query oracle list-oracle-vote
Miss Counter
Query the missed vote count for a validator.
message QueryGetMissCounterRequest {
string validator = 1;
}
message QueryGetMissCounterResponse {
MissCounter miss_counter = 1;
}
Use the following CLI command:
oracled query oracle show-miss-counter [validator]
Miss Counters
Query the missed vote counts for all validators.
message QueryAllMissCounterRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}
message QueryAllMissCounterResponse {
repeated MissCounter miss_counter = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
Use the following CLI command:
oracled query oracle list-miss-counter
Feeder Delegation
Query the feeder delegation for a validator.
message QueryGetFeederDelegationRequest {
string validator = 1;
}
message QueryGetFeederDelegationResponse {
FeederDelegation feeder_delegation = 1;
}
Use the following CLI command:
oracled query oracle show-feeder-delegation [validator]
Feeder Delegations
Query the feeder delegations for all validators.
message QueryAllFeederDelegationRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}
message QueryAllFeederDelegationResponse {
repeated FeederDelegation feeder_delegation = 1;
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
Use the following CLI command:
oracled query oracle list-feeder-delegation
Events
EventOraclePreVote
triggers when a pre-vote is successful:
message EventOraclePreVote {
string validator = 1;
string feeder = 2;
}
EventOracleVote
triggers when a vote is successful:
message EventOracleVote {
string feeder = 1;
OracleVote oracle_vote = 2;
}
EventDelegateFeedConsent
triggers when a delegate action is successful:
message EventDelegateFeedConsent {
string validator = 1;
string feeder = 2;
}
EventVoteIntervalEnds
triggers when the final block of a VotePeriod
is reached:
message EventVoteIntervalEnds {
int64 time_millis = 1;
int64 block_height = 2;
int64 vote_period = 3;
repeated ValidatorVoteIntervalSummary validator_summaries = 4 [(gogoproto.nullable) = false];
int64 previous_vote_interval_end_time_millis = 5;
repeated BallotVoteResult ballot_vote_results = 6 [(gogoproto.nullable) = false];
}
message ValidatorVoteIntervalSummary {
string validator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
int64 validator_power = 2;
bool voted = 3;
int64 vote_interval_miss_counter = 4;
int64 slash_window_miss_counter = 5;
repeated cosmos.base.v1beta1.Coin rewards = 6 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
message BallotVoteResult {
string namespace = 1;
string module = 2;
bool quorum_reached = 3;
int64 ballot_power = 4;
bool majority_achieved = 5;
VoteType majority_vote_type = 6;
string majority_vote_payload = 7;
// error returned by a call to the corresponding module's OnMajorityVote callback method
string callback_error = 8;
}
The EventSlashWindowEnds
event is triggered at the end of a SlashWindow
:
message EventSlashWindowEnds {
int64 slash_window = 1;
repeated ValidatorSlashWindowSummary validator_summaries = 2 [(gogoproto.nullable) = false];
}
message ValidatorSlashWindowSummary {
string validator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
int64 validator_power = 2;
int64 miss_counter = 3;
bool jailed = 4;
string slash_amount = 5 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}
EventQuorumNotReached
is activated if the sum of all votes for a <module,namespace>
pair doesn't meet the quorum
threshold:
message EventQuorumNotReached {
string module = 1;
string namespace = 2;
string ballot_power = 3;
}
EventMajorityNotAchieved
is activated if the sum of the validators' voting power who voted for a specific payload of
a <module,namespace>
pair, doesn't meet the majority threshold:
message EventMajorityNotAchieved {
string module = 1;
string namespace = 2;
}
EventInvalidMajorityVotePayload
is activated when a majority vote payload is found to be invalid (e.g., cannot be
parsed) by any oracle callback:
message EventInvalidMajorityVotePayload {
string module = 1;
string namespace = 2;
string majority_vote_payload = 3;
string error = 4;
}
State
OraclePreVote
The Oracle module maintains an OraclePreVote
object in the state to store the latest pre-vote data for each validator:
message OraclePreVote {
string validator = 1;
string hash = 2;
int64 submit_block = 3;
}
The OraclePreVote
for a validator is stored using a key with this format:
[]byte("v1/oracle-pre-vote/") | []byte(lengthPrefixedValidatorAddress)
OracleVote
The Oracle module maintains an OracleVote
object in the state to store the latest vote data for each validator:
message OracleVote {
string validator = 1;
repeated ModuleVote module_votes = 2;
}
The OracleVote
for a validator is stored using a key with this format:
[]byte("v1/oracle-vote/") | []byte(lengthPrefixedValidatorAddress)
FeederDelegation
The Oracle module maintains a FeederDelegation
object in the state to associate a feeder with a validator.
message FeederDelegation {
string validator = 1;
string feeder = 2;
}
The FeederDelegation
for a validator is stored with a key constructed as:
[]byte("v1/feeder-delegation/") | []byte(lengthPrefixedValidatorAddress)
MissCounter
The oracle module maintains a MissCounter
object in the state to track a validator's missed votes count.
message MissCounter {
string validator = 1;
int64 counter = 2;
}
The MissCounter
for a validator is stored with a key constructed as:
[]byte("v1/miss-counter/") | []byte(lengthPrefixedValidatorAddress)
Parameters
message Params {
int64 vote_period = 1;
string quorum = 2;
string vote_threshold = 3;
string slash_fraction = 4;
int64 slash_window = 5;
string max_miss_rate_per_slash_window = 6;
string max_miss_rate_per_vote_period = 7;
// Ratio determining the amount of collected fees to distribute among validators as oracle reward.
string fee_collector_reward_ratio = 8;
}
VotePeriod
Defines the number of blocks during which voting happens.
- Type:
int64
- Range: [1, ∞)
- Default:
5 blockchain blocks
Quorum
Minimum ratio of voting power to total power required for ballot approval.
- Type:
Dec
- Range: [0.33, 1]
- Default: 0.5
VoteThreshold
Minimum ratio of votes required for a specific rate to be deemed as the majority-selected rate.
- Type:
Dec
- Range: (0.5, 1]
- Default: 0.51
SlashFraction
- Type:
Dec
- Range: [0, 1]
- Default: 0.01%
Specifies the penalty ratio on bonded tokens.
SlashWindow
- Type:
int64
- Range: [1, ∞)
- Default:
BlocksPerWeek
Defines the number of blocks for slashing.
MaxMissRatePerSlashWindow
Ratio of maximum allowed missed votes per slash window to avoid slashing.
- Type:
Dec
- Range: [0, 1]
- Default: 0.95
MaxMissRatePerVotePeriod
Ratio of maximum allowed missed votes per vote period to prevent MissCounter
increment.
- Type:
Dec
- Range: [0, 1]
- Default: 0
FeeCollectorRewardRatio
Ratio determining the portion of feeCollector spendable coins reserved for oracle rewards.
- Type:
Dec
- Range: [0, 1]
- Default: 0.1
Genesis
The genesis state for the oracle module may contain Parameters values. The complete structure of the genesis state is:
message GenesisState {
Params params = 1
repeated FeederDelegation feeder_delegation_list = 2
repeated OraclePreVote oracle_pre_vote_list = 3
repeated OracleVote oracle_vote_list = 4
repeated MissCounter miss_counter_list = 5
}