From 7b25aa37eafa90169b4d5c4030d5d36fc38d4be6 Mon Sep 17 00:00:00 2001 From: Leon <156270887+leonz789@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:51:18 +0800 Subject: [PATCH] introduce slashing for oracle service (#224) * introduce slashing for oracle service * add slashing info for genesis, export/init * Update x/oracle/types/events.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update x/oracle/types/genesis_test.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix typos, duplicate imports * typos * lint * lint, optimize * lint * Update x/oracle/keeper/slashing.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix nil worker * perf: return immediately before loop ends * refactor(oracle) * fix(oracle): dont save same value multiple times * typos, int64->uint64 * typo * fix(oracle):keep nonce, filter for slashing calculation * lint --------- Co-authored-by: X Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- app/app.go | 1 + proto/exocore/oracle/v1/genesis.proto | 29 +- proto/exocore/oracle/v1/params.proto | 41 +- proto/exocore/oracle/v1/slashing.proto | 17 + testutil/keeper/oracle.go | 2 + x/delegation/keeper/abci.go | 1 + x/delegation/keeper/delegation_state.go | 1 + x/operator/keeper/slash.go | 3 +- x/operator/keeper/usd_value.go | 1 - x/oracle/genesis.go | 32 +- x/oracle/keeper/aggregator/aggregator.go | 35 +- x/oracle/keeper/aggregator/calculator.go | 1 - x/oracle/keeper/aggregator/context.go | 113 +++- x/oracle/keeper/aggregator/worker.go | 70 ++- x/oracle/keeper/common/expected_keepers.go | 9 +- x/oracle/keeper/keeper.go | 3 + x/oracle/keeper/msg_server_create_price.go | 1 - x/oracle/keeper/slashing.go | 153 +++++ x/oracle/module.go | 174 +++++- x/oracle/types/events.go | 13 +- x/oracle/types/expected_keepers.go | 6 + x/oracle/types/genesis.go | 7 + x/oracle/types/genesis.pb.go | 645 +++++++++++++++++++-- x/oracle/types/genesis_test.go | 12 +- x/oracle/types/key_slashing.go | 20 + x/oracle/types/params.go | 36 ++ x/oracle/types/params.pb.go | 554 ++++++++++++++++-- x/oracle/types/slashing.pb.go | 431 ++++++++++++++ x/oracle/types/types.go | 9 + 29 files changed, 2310 insertions(+), 110 deletions(-) create mode 100644 proto/exocore/oracle/v1/slashing.proto create mode 100644 x/oracle/keeper/slashing.go create mode 100644 x/oracle/types/key_slashing.go create mode 100644 x/oracle/types/slashing.pb.go diff --git a/app/app.go b/app/app.go index 70530e4bc..46fdd22a8 100644 --- a/app/app.go +++ b/app/app.go @@ -588,6 +588,7 @@ func NewExocoreApp( appCodec, keys[oracleTypes.StoreKey], memKeys[oracleTypes.MemStoreKey], app.GetSubspace(oracleTypes.ModuleName), app.StakingKeeper, &app.DelegationKeeper, &app.AssetsKeeper, authAddrString, + &app.SlashingKeeper, ) // the SDK slashing module is used to slash validators in the case of downtime. it tracks diff --git a/proto/exocore/oracle/v1/genesis.proto b/proto/exocore/oracle/v1/genesis.proto index 512db1f02..9be9313f8 100644 --- a/proto/exocore/oracle/v1/genesis.proto +++ b/proto/exocore/oracle/v1/genesis.proto @@ -9,6 +9,7 @@ import "exocore/oracle/v1/params.proto"; import "exocore/oracle/v1/prices.proto"; import "exocore/oracle/v1/recent_msg.proto"; import "exocore/oracle/v1/recent_params.proto"; +import "exocore/oracle/v1/slashing.proto"; import "exocore/oracle/v1/validator_update_block.proto"; import "gogoproto/gogo.proto"; @@ -18,10 +19,12 @@ option go_package = "github.com/ExocoreNetwork/exocore/x/oracle/types"; message GenesisState { // module params Params params = 1 [(gogoproto.nullable) = false]; - // prices of all tokens + + // prices of all tokens including NST repeated Prices prices_list = 2 [(gogoproto.nullable) = false]; //TODO: userDefinedTokenFeeder + // information for memory-cache recovery // latest block on which the validator set be updated ValidatorUpdateBlock validator_update_block = 3; // index for the cached recent params @@ -32,10 +35,18 @@ message GenesisState { repeated RecentMsg recent_msg_list = 6[(gogoproto.nullable) = false]; // cached recent params repeated RecentParams recent_params_list = 7[(gogoproto.nullable) = false]; + + // information for NST related // stakerInfos for each nst token repeated StakerInfosAssets staker_infos_assets = 8[(gogoproto.nullable) = false]; // stakerList for each nst token repeated StakerListAssets staker_list_assets = 9[(gogoproto.nullable) = false]; + + // information for slashing history + // ValidatorReportInfo records all the validatorReportInfos + repeated ValidatorReportInfo validator_report_infos = 10[(gogoproto.nullable)=false]; + // ValidatorMissedRounds records missedRounds for all validators seen + repeated ValidatorMissedRounds validator_missed_rounds = 11[(gogoproto.nullable)=false]; } // stakerInfosAssets bond stakerinfos to their related assets id @@ -53,3 +64,19 @@ message StakerListAssets { // stakerList StakerList staker_list = 2; } + +// ValidatorMissedRounds record missed rounds indexes for a validator which consAddr corresponding to the address +message ValidatorMissedRounds { + // address of validator + string address = 1; + // missed_rounds tells how many rounds this validtor had missed for current windo + repeated MissedRound missed_rounds = 2; +} + +// MissedRound records if round with index is missed +message MissedRound { + // index of the round in current window + uint64 index = 1; + // if this round is missed + bool missed = 2; +} diff --git a/proto/exocore/oracle/v1/params.proto b/proto/exocore/oracle/v1/params.proto index 1407a062d..7e982755d 100644 --- a/proto/exocore/oracle/v1/params.proto +++ b/proto/exocore/oracle/v1/params.proto @@ -1,10 +1,11 @@ syntax = "proto3"; package exocore.oracle.v1; +import "amino/amino.proto"; import "exocore/oracle/v1/info.proto"; import "exocore/oracle/v1/token_feeder.proto"; import "gogoproto/gogo.proto"; - +import "google/protobuf/duration.proto"; option go_package = "github.com/ExocoreNetwork/exocore/x/oracle/types"; // Params defines the parameters for the module. @@ -28,10 +29,13 @@ message Params { int32 threshold_b = 8; // for v1, mode=1, get final price as soon as voting power reach threshold_a/threshold_b ConsensusMode mode = 9; - // for each round, a validator only allowed to provide at most max_det_id continuos rounds of prices for DS + // for each round, a validator only allowed to provide at most max_det_id continuous rounds of prices for DS int32 max_det_id = 10; // for each token, only keep max_size_prices round of prices int32 max_size_prices = 11; + + // slashing defines the slashing related params + SlashingParams slashing = 12; } // ConsensusMode defines the consensus mode for the prices. @@ -42,4 +46,35 @@ enum ConsensusMode { // CONSENSUS_MODE_ASAP defines the mode to get final price immediately when the voting power // exceeds the threshold. CONSENSUS_MODE_ASAP = 1 [(gogoproto.enumvalue_customname) = "ConsensusModeASAP"]; -} \ No newline at end of file +} + +// slashing related params +message SlashingParams { + // reported_rounds_window defines how many rounds included in one window for performance review of missing report + int64 reported_rounds_window = 1; + // min_reported_perwindow defines at least how many rounds should be reported, this is a percentage of window + bytes min_reported_per_window = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; + // oracle_miss_jail_duration defines the duration one validator should be jailed for missing reporting price + google.protobuf.Duration oracle_miss_jail_duration = 3 + [(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true]; + // oracle_malicious_jail_duration defines the duratin one validator should be jailed for malicious behavior + google.protobuf.Duration oracle_malicious_jail_duration =4 + [(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true]; + // slash_fraction_miss defines the fraction one validator should be punished for msissing reporting price + bytes slash_fraction_miss = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; + // slash_fraction_miss defines the fraction one validator should be punished for malicious behavior + bytes slash_fraction_malicious = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; + +} diff --git a/proto/exocore/oracle/v1/slashing.proto b/proto/exocore/oracle/v1/slashing.proto new file mode 100644 index 000000000..a74d076a9 --- /dev/null +++ b/proto/exocore/oracle/v1/slashing.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package exocore.oracle.v1; + +option go_package = "github.com/ExocoreNetwork/exocore/x/oracle/types"; + +// ValidatorReportInfo represents the information to describe the miss status of a validator reporting prices +message ValidatorReportInfo { + // address of the validtor + string address = 1; + // start_height for the performance round of the configured window of rounds + int64 start_height = 2; + // index_offset track the offset of current window + int64 index_offset = 3; + // missed_rounds_counter counts the number of missed rounds for this window + int64 missed_rounds_counter = 4; +} diff --git a/testutil/keeper/oracle.go b/testutil/keeper/oracle.go index 4308d54df..646eb86f9 100644 --- a/testutil/keeper/oracle.go +++ b/testutil/keeper/oracle.go @@ -21,6 +21,7 @@ import ( assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper" delegationkeeper "github.com/ExocoreNetwork/exocore/x/delegation/keeper" dogfoodkeeper "github.com/ExocoreNetwork/exocore/x/dogfood/keeper" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/stretchr/testify/require" ) @@ -52,6 +53,7 @@ func OracleKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { delegationkeeper.Keeper{}, assetskeeper.Keeper{}, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + slashingkeeper.Keeper{}, ) ctx := sdk.NewContext(stateStore, tmproto.Header{ diff --git a/x/delegation/keeper/abci.go b/x/delegation/keeper/abci.go index 47ca02748..d89be5616 100644 --- a/x/delegation/keeper/abci.go +++ b/x/delegation/keeper/abci.go @@ -116,6 +116,7 @@ func (k *Keeper) EndBlock( continue } + // TODO: the field IsPending in types.UndelegationRecord is useless since when a record is completed it will be removed, so the record is either existing&pending or unexist&completed, and the IsPending is not used nowhere(like slashFromUndelegation doesn't check this field either), good to remove this field. And types.UndelegationRecord is actually PendingUndelegationRecord // delete the Undelegation records that have been complemented err = k.DeleteUndelegationRecord(cc, record) if err != nil { diff --git a/x/delegation/keeper/delegation_state.go b/x/delegation/keeper/delegation_state.go index ce04b35c9..106324436 100644 --- a/x/delegation/keeper/delegation_state.go +++ b/x/delegation/keeper/delegation_state.go @@ -303,6 +303,7 @@ func (k *Keeper) SetStakerShareToZero(ctx sdk.Context, operator, assetID string, singleStateKey := assetstype.GetJoinedStoreKey(stakerID, assetID, operator) value := store.Get(singleStateKey) if value != nil { + // TODO: check if pendingUndelegation==0 => just delete this item instead of update share to zero, otherwise this item will be left in the storage forever with zero value delegationState := delegationtype.DelegationAmounts{} k.cdc.MustUnmarshal(value, &delegationState) delegationState.UndelegatableShare = sdkmath.LegacyZeroDec() diff --git a/x/operator/keeper/slash.go b/x/operator/keeper/slash.go index ab6188f06..3393fc166 100644 --- a/x/operator/keeper/slash.go +++ b/x/operator/keeper/slash.go @@ -22,7 +22,7 @@ func GetSlashIDForDogfood(infraction stakingtypes.Infraction, infractionHeight i return strings.Join([]string{hexutil.EncodeUint64(uint64(infraction)), hexutil.EncodeUint64(uint64(infractionHeight))}, utils.DelimiterForID) } -// SlashFromUndelegation executes the slash from an undelegation +// SlashFromUndelegation executes the slash from an undelegation, reduce the .ActualCompletedAmount from undelegationRecords func SlashFromUndelegation(undelegation *delegationtype.UndelegationRecord, slashProportion sdkmath.LegacyDec) *types.SlashFromUndelegation { if undelegation.ActualCompletedAmount.IsZero() { return nil @@ -135,6 +135,7 @@ func (k *Keeper) SlashAssets(ctx sdk.Context, snapshotHeight int64, parameter *t state.OperatorShare = sdkmath.LegacyZeroDec() } state.TotalAmount = remainingAmount + // TODO: check if pendingUndelegation also zero => delete this item, and this operator should be opted out if all aasets falls to 0 since the miniself is not satisfied then. executionInfo.SlashAssetsPool = append(executionInfo.SlashAssetsPool, types.SlashFromAssetsPool{ AssetID: assetID, Amount: slashAmount, diff --git a/x/operator/keeper/usd_value.go b/x/operator/keeper/usd_value.go index b277bec05..10023b95a 100644 --- a/x/operator/keeper/usd_value.go +++ b/x/operator/keeper/usd_value.go @@ -372,7 +372,6 @@ func (k *Keeper) CalculateUSDValueForOperator( } // iterate all assets owned by the operator to calculate its voting power opFuncToIterateAssets := func(assetID string, state *assetstype.OperatorAssetInfo) error { - // var price operatortypes.Price var price oracletype.Price var decimal uint32 if isForSlash { diff --git a/x/oracle/genesis.go b/x/oracle/genesis.go index 084caa580..3dc8994e9 100644 --- a/x/oracle/genesis.go +++ b/x/oracle/genesis.go @@ -40,6 +40,16 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) for _, elem := range genState.StakerInfosAssets { k.SetStakerInfos(ctx, elem.AssetId, elem.StakerInfos) } + // set validatorReportInfos + for _, elem := range genState.ValidatorReportInfos { + k.SetValidatorReportInfo(ctx, elem.Address, elem) + } + // set validatorMissedRounds + for _, elem := range genState.ValidatorMissedRounds { + for _, missedRound := range elem.MissedRounds { + k.SetValidatorMissedRoundBitArray(ctx, elem.Address, missedRound.Index, missedRound.Missed) + } + } // this line is used by starport scaffolding # genesis/module/init k.SetParams(ctx, genState.Params) } @@ -47,9 +57,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) // ExportGenesis returns the module's exported genesis func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() + // params genesis.Params = k.GetParams(ctx) + // priceList genesis.PricesList = k.GetAllPrices(ctx) + + // cache recovery related, used by agc // Get all validatorUpdateBlock validatorUpdateBlock, found := k.GetValidatorUpdateBlock(ctx) if found { @@ -67,9 +81,25 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { } genesis.RecentMsgList = k.GetAllRecentMsg(ctx) genesis.RecentParamsList = k.GetAllRecentParams(ctx) - // TODO: export stakerListAssets, and stakerInfosAssets + + // NST related genesis.StakerInfosAssets = k.GetAllStakerInfosAssets(ctx) genesis.StakerListAssets = k.GetAllStakerListAssets(ctx) + + // slashing related + reportInfos := make([]types.ValidatorReportInfo, 0) + validatorMissedRounds := make([]types.ValidatorMissedRounds, 0) + k.IterateValidatorReportInfos(ctx, func(validator string, reportInfo types.ValidatorReportInfo) bool { + reportInfos = append(reportInfos, reportInfo) + missedRounds := k.GetValidatorMissedRounds(ctx, validator) + validatorMissedRounds = append(validatorMissedRounds, types.ValidatorMissedRounds{ + Address: validator, + MissedRounds: missedRounds, + }) + return false + }) + genesis.ValidatorReportInfos = reportInfos + genesis.ValidatorMissedRounds = validatorMissedRounds // this line is used by starport scaffolding # genesis/module/export return genesis diff --git a/x/oracle/keeper/aggregator/aggregator.go b/x/oracle/keeper/aggregator/aggregator.go index 48e7778cd..ca17889d5 100644 --- a/x/oracle/keeper/aggregator/aggregator.go +++ b/x/oracle/keeper/aggregator/aggregator.go @@ -2,6 +2,7 @@ package aggregator import ( "math/big" + "sort" "github.com/ExocoreNetwork/exocore/x/oracle/keeper/common" "github.com/ExocoreNetwork/exocore/x/oracle/types" @@ -124,6 +125,7 @@ func (agg *aggregator) fillPrice(pSources []*types.PriceSource, validator string pTR.price = new(big.Int).Set(priceTmp.price) pTR.detRoundID = priceTmp.detRoundID pTR.timestamp = priceTmp.timestamp + break } } } @@ -151,7 +153,7 @@ func (agg *aggregator) confirmDSPrice(confirmedRounds []*confirmedPrice) { price.detRoundID = priceSourceRound.detID price.timestamp = priceSourceRound.timestamp price.price = priceSourceRound.price - } // else TODO: panice in V1 + } // else TODO: panic in V1 } } } @@ -191,6 +193,37 @@ func (agg *aggregator) aggregate() *big.Int { return agg.finalPrice } +// TODO: this only suites for DS. check source type for extension +// GetFinaPriceListForFeederIDs retrieve final price info as an array ordered by sourceID asc +func (agg *aggregator) getFinalPriceList(feederID uint64) []*types.AggFinalPrice { + sourceIDs := make([]uint64, 0, len(agg.dsPrices)) + for sID := range agg.dsPrices { + sourceIDs = append(sourceIDs, sID) + } + sort.Slice(sourceIDs, func(i, j int) bool { + return sourceIDs[i] < sourceIDs[j] + }) + ret := make([]*types.AggFinalPrice, 0, len(sourceIDs)) + for _, sID := range sourceIDs { + for _, report := range agg.reports { + price := report.prices[sID] + if price == nil || price.detRoundID != agg.dsPrices[sID] { + // the DetID mismatch should not happen + continue + } + ret = append(ret, &types.AggFinalPrice{ + FeederID: feederID, + SourceID: sID, + DetID: price.detRoundID, + Price: price.price.String(), + }) + // {feederID, sourceID} has been found, skip rest reports + break + } + } + return ret +} + func newAggregator(validatorSetLength int, totalPower *big.Int) *aggregator { return &aggregator{ reports: make([]*reportPrice, 0, validatorSetLength), diff --git a/x/oracle/keeper/aggregator/calculator.go b/x/oracle/keeper/aggregator/calculator.go index 9ee504d25..201285ce2 100644 --- a/x/oracle/keeper/aggregator/calculator.go +++ b/x/oracle/keeper/aggregator/calculator.go @@ -52,7 +52,6 @@ func (r *roundPrices) updatePriceAndPower(pw *priceAndPower, totalPower *big.Int updated = true if common.ExceedsThreshold(pw.power, totalPower) { r.price = pw.price - // r.confirmed = true confirmed = true } } diff --git a/x/oracle/keeper/aggregator/context.go b/x/oracle/keeper/aggregator/context.go index 591f653d5..162f16bde 100644 --- a/x/oracle/keeper/aggregator/context.go +++ b/x/oracle/keeper/aggregator/context.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math/big" + "sort" "github.com/ExocoreNetwork/exocore/x/oracle/keeper/cache" "github.com/ExocoreNetwork/exocore/x/oracle/keeper/common" @@ -127,10 +128,20 @@ func (agc *AggregatorContext) checkMsg(msg *types.MsgCreatePrice) error { // check feeder is active feederContext := agc.rounds[msg.FeederID] - if feederContext == nil || feederContext.status != roundStatusOpen { - // feederId does not exist or not alive - return errors.New("context not exist or not available") + if feederContext == nil { + return fmt.Errorf("context not exist for feederID:%d", msg.FeederID) } + // This round had been sealed but current window not closed + if feederContext.status != roundStatusOpen { + if feederWorker := agc.aggregators[msg.FeederID]; feederWorker != nil { + if _, list4Aggregator := feederWorker.filtrate(msg); list4Aggregator != nil { + // record this message for performance evaluation(used for slashing) + feederWorker.recordMessage(msg.Creator, msg.FeederID, list4Aggregator) + } + } + return fmt.Errorf("context is available for feederID:%d", msg.FeederID) + } + // senity check on basedBlock if msg.BasedBlock != feederContext.basedBlock { return errors.New("baseblock not match") @@ -160,10 +171,16 @@ func (agc *AggregatorContext) FillPrice(msg *types.MsgCreatePrice) (*PriceItemKV } if feederWorker.sealed { + if _, list4Aggregator := feederWorker.filtrate(msg); list4Aggregator != nil { + // record this message for performance evaluation(used for slashing) + feederWorker.recordMessage(msg.Creator, msg.FeederID, list4Aggregator) + } return nil, nil, types.ErrPriceProposalIgnored.Wrap("price aggregation for this round has sealed") } if listFilled := feederWorker.do(msg); listFilled != nil { + // record this message for performance evaluation(used for slashing) + feederWorker.recordMessage(msg.Creator, msg.FeederID, listFilled) if finalPrice := feederWorker.aggregate(); finalPrice != nil { agc.rounds[msg.FeederID].status = roundStatusClosed feederWorker.seal() @@ -196,26 +213,46 @@ func (agc *AggregatorContext) NewCreatePrice(_ sdk.Context, msg *types.MsgCreate // including possible aggregation and state update // when validatorSet update, set force to true, to seal all alive round // returns: 1st successful sealed, need to be written to KVStore, 2nd: failed sealed tokenID, use previous price to write to KVStore -func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success []*PriceItemKV, failed []uint64, sealed []uint64) { - for feederID, round := range agc.rounds { +func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success []*PriceItemKV, failed []uint64, sealed []uint64, windowClosed []uint64) { + feederIDs := make([]uint64, 0, len(agc.rounds)) + for fID := range agc.rounds { + feederIDs = append(feederIDs, fID) + } + sort.Slice(feederIDs, func(i, j int) bool { + return feederIDs[i] < feederIDs[j] + }) + height := uint64(ctx.BlockHeight()) + // make sure feederIDs are accessed in order to calculate the indexOffset for slashing + windowClosedMap := make(map[uint64]bool) + for _, feederID := range feederIDs { + if agc.windowEnd(feederID, height) { + windowClosed = append(windowClosed, feederID) + windowClosedMap[feederID] = true + } + round := agc.rounds[feederID] if round.status == roundStatusOpen { feeder := agc.params.GetTokenFeeder(feederID) // TODO: for mode=1, we don't do aggregate() here, since if it donesn't success in the transaction execution stage, it won't success here // but it's not always the same for other modes, switch modes switch common.Mode { case types.ConsensusModeASAP: - expired := feeder.EndBlock > 0 && uint64(ctx.BlockHeight()) >= feeder.EndBlock - outOfWindow := uint64(ctx.BlockHeight())-round.basedBlock >= uint64(common.MaxNonce) + offset := height - round.basedBlock + expired := feeder.EndBlock > 0 && height >= feeder.EndBlock + outOfWindow := offset >= uint64(common.MaxNonce) + + // an open round reach its end of window, increase offsetIndex for active valdiator and chech the performance(missing/malicious) + if expired || outOfWindow || force { failed = append(failed, feeder.TokenID) - if expired { - delete(agc.rounds, feederID) - } else { + if !expired { round.status = roundStatusClosed } // TODO: optimize operformance sealed = append(sealed, feederID) - delete(agc.aggregators, feederID) + if !windowClosedMap[feederID] { + // this should be clear after performanceReview + agc.RemoveWorker(feederID) + } } default: ctx.Logger().Info("mode other than 1 is not support now") @@ -223,11 +260,10 @@ func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success [] } // all status: 1->2, remove its aggregator if agc.aggregators[feederID] != nil && agc.aggregators[feederID].sealed { - delete(agc.aggregators, feederID) sealed = append(sealed, feederID) } } - return success, failed, sealed + return success, failed, sealed, windowClosed } // PrepareEndBlock is called at EndBlock stage, to prepare the roundInfo for the next block(of input block) @@ -279,9 +315,10 @@ func (agc *AggregatorContext) PrepareRoundEndBlock(block uint64) (newRoundFeeder // set nonce for corresponding feederID for new roud start newRoundFeederIDs = append(newRoundFeederIDs, feederIDUint64) // drop previous worker - delete(agc.aggregators, feederIDUint64) + agc.RemoveWorker(feederIDUint64) } else if round.status == roundStatusOpen && left >= uint64(common.MaxNonce) { // this shouldn't happen, if do sealround properly before prepareRound, basically for test only + // TODO: print error log here round.status = roundStatusClosed // TODO: just modify the status here, since sealRound should do all the related seal actions already when parepare invoked } @@ -335,6 +372,54 @@ func (agc *AggregatorContext) GetParamsMaxSizePrices() uint64 { return uint64(agc.params.MaxSizePrices) } +// GetFinalPriceListForFeederIDs get final price list for required feederIDs in format []{feederID, sourceID, detID, price} with asc of {feederID, sourceID} +// feederIDs is required to be ordered asc +func (agc *AggregatorContext) GetFinalPriceListForFeederIDs(feederIDs []uint64) []*types.AggFinalPrice { + ret := make([]*types.AggFinalPrice, 0, len(feederIDs)) + for _, feederID := range feederIDs { + feederWorker := agc.aggregators[feederID] + if feederWorker != nil { + if pList := feederWorker.getFinalPriceList(feederID); len(pList) > 0 { + ret = append(ret, pList...) + } + } + } + return ret +} + +// PerformanceReview compare results to decide whether the validator is effective, honest +func (agc *AggregatorContext) PerformanceReview(ctx sdk.Context, finalPrice *types.AggFinalPrice, validator string) (exist, matched bool) { + feederWorker := agc.aggregators[finalPrice.FeederID] + if feederWorker == nil { + // Log unexpected nil feederWorker for debugging + ctx.Logger().Error( + "unexpected nil feederWorker in PerformanceReview", + "feederID", finalPrice.FeederID, + "validator", validator, + ) + // Treat validator as effective & honest to avoid unfair penalties + exist = true + matched = true + return + } + exist, matched = feederWorker.check(validator, finalPrice.FeederID, finalPrice.SourceID, finalPrice.Price, finalPrice.DetID) + return +} + +func (agc AggregatorContext) windowEnd(feederID, height uint64) bool { + feeder := agc.params.TokenFeeders[feederID] + if (feeder.EndBlock > 0 && feeder.EndBlock <= height) || feeder.StartBaseBlock > height { + return false + } + delta := height - feeder.StartBaseBlock + left := delta % feeder.Interval + return left == uint64(common.MaxNonce) +} + +func (agc *AggregatorContext) RemoveWorker(feederID uint64) { + delete(agc.aggregators, feederID) +} + // NewAggregatorContext returns a new instance of AggregatorContext func NewAggregatorContext() *AggregatorContext { return &AggregatorContext{ diff --git a/x/oracle/keeper/aggregator/worker.go b/x/oracle/keeper/aggregator/worker.go index bbf7dcf2f..a32805854 100644 --- a/x/oracle/keeper/aggregator/worker.go +++ b/x/oracle/keeper/aggregator/worker.go @@ -20,14 +20,75 @@ type worker struct { // when enough data(exceeds threshold) collected, aggregate to conduct the final price a *aggregator ctx *AggregatorContext + // TODO: move outside into context through .ctx + records recordMsg } -func (w *worker) do(msg *types.MsgCreatePrice) []*types.PriceSource { - accAddress, _ := sdk.AccAddressFromBech32(msg.Creator) +// recordKey used to retrieve messages from records to evaluate that if a validator report proper price for a specific feederID+sourceID +type recordKey struct { + validator string + feederID uint64 + sourceID uint64 +} + +// recordMsg define wrap the map for fast access to validator's message info +type recordMsg map[recordKey][]*types.PriceTimeDetID + +func newRecordMsg() recordMsg { + return make(map[recordKey][]*types.PriceTimeDetID) +} + +func (r recordMsg) get(validator string, feederID, sourceID uint64) []*types.PriceTimeDetID { + v := r[recordKey{validator, feederID, sourceID}] + return v +} + +func (r recordMsg) check(validator string, feederID, sourceID uint64, price, detID string) (exist, matched bool) { + prices := r.get(validator, feederID, sourceID) + for _, p := range prices { + if p.DetID == detID { + exist = true + if p.Price == price { + matched = true + return + } + } + } + return +} + +func (r recordMsg) set(creator string, feederID uint64, priceSources []*types.PriceSource) { + accAddress, _ := sdk.AccAddressFromBech32(creator) validator := sdk.ConsAddress(accAddress).String() - power := w.ctx.validatorsPower[validator] + for _, price := range priceSources { + r[recordKey{validator, feederID, price.SourceID}] = price.Prices + } +} + +// GetFinalPriceList relies requirement to aggregator inside them to get final price list +// []{feederID, sourceID, detID, price} in asc order of {soruceID} +func (w *worker) getFinalPriceList(feederID uint64) []*types.AggFinalPrice { + return w.a.getFinalPriceList(feederID) +} + +func (w *worker) filtrate(msg *types.MsgCreatePrice) (list4Calculator []*types.PriceSource, list4Aggregator []*types.PriceSource) { + return w.f.filtrate(msg) +} + +func (w *worker) recordMessage(creator string, feederID uint64, priceSources []*types.PriceSource) { + w.records.set(creator, feederID, priceSources) +} + +func (w *worker) check(validator string, feederID, sourceID uint64, price, detID string) (exist, matched bool) { + return w.records.check(validator, feederID, sourceID, price, detID) +} + +func (w *worker) do(msg *types.MsgCreatePrice) []*types.PriceSource { list4Calculator, list4Aggregator := w.f.filtrate(msg) if list4Aggregator != nil { + accAddress, _ := sdk.AccAddressFromBech32(msg.Creator) + validator := sdk.ConsAddress(accAddress).String() + power := w.ctx.validatorsPower[validator] w.a.fillPrice(list4Aggregator, validator, power) if confirmedRounds := w.c.fillPrice(list4Calculator, validator, power); confirmedRounds != nil { w.a.confirmDSPrice(confirmedRounds) @@ -47,9 +108,7 @@ func (w *worker) seal() { } w.sealed = true w.price = w.a.aggregate().String() - w.f = nil w.c = nil - w.a = nil } // newWorker new a instance for a tokenFeeder's specific round @@ -60,5 +119,6 @@ func newWorker(feederID uint64, agc *AggregatorContext) *worker { a: newAggregator(len(agc.validatorsPower), agc.totalPower), decimal: agc.params.GetTokenInfo(feederID).Decimal, ctx: agc, + records: newRecordMsg(), } } diff --git a/x/oracle/keeper/common/expected_keepers.go b/x/oracle/keeper/common/expected_keepers.go index 6e68f8695..f668d139b 100644 --- a/x/oracle/keeper/common/expected_keepers.go +++ b/x/oracle/keeper/common/expected_keepers.go @@ -7,7 +7,7 @@ import ( "github.com/ExocoreNetwork/exocore/x/oracle/types" abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) type Price struct { @@ -53,9 +53,12 @@ var _ KeeperDogfood = dogfoodkeeper.Keeper{} type KeeperDogfood = interface { GetLastTotalPower(ctx sdk.Context) sdkmath.Int - IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator stakingTypes.ValidatorI) (stop bool)) + IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator stakingtypes.ValidatorI) (stop bool)) GetValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate - GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator stakingTypes.Validator, found bool) + GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator stakingtypes.Validator, found bool) GetAllExocoreValidators(ctx sdk.Context) (validators []dogfoodtypes.ExocoreValidator) + ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) stakingtypes.ValidatorI + SlashWithInfractionReason(ctx sdk.Context, addr sdk.ConsAddress, infractionHeight, power int64, slashFactor sdk.Dec, infraction stakingtypes.Infraction) sdkmath.Int + Jail(ctx sdk.Context, addr sdk.ConsAddress) } diff --git a/x/oracle/keeper/keeper.go b/x/oracle/keeper/keeper.go index 8ec37b5f9..161ab3b42 100644 --- a/x/oracle/keeper/keeper.go +++ b/x/oracle/keeper/keeper.go @@ -25,6 +25,7 @@ type ( common.KeeperDogfood delegationKeeper types.DelegationKeeper assetsKeeper types.AssetsKeeper + types.SlashingKeeper } ) @@ -39,6 +40,7 @@ func NewKeeper( delegationKeeper types.DelegationKeeper, assetsKeeper types.AssetsKeeper, authority string, + slashingKeeper types.SlashingKeeper, ) Keeper { // ensure authority is a valid bech32 address if _, err := sdk.AccAddressFromBech32(authority); err != nil { @@ -58,6 +60,7 @@ func NewKeeper( delegationKeeper: delegationKeeper, assetsKeeper: assetsKeeper, authority: authority, + SlashingKeeper: slashingKeeper, } } diff --git a/x/oracle/keeper/msg_server_create_price.go b/x/oracle/keeper/msg_server_create_price.go index 7eca28a82..fce7d900f 100644 --- a/x/oracle/keeper/msg_server_create_price.go +++ b/x/oracle/keeper/msg_server_create_price.go @@ -58,7 +58,6 @@ func (ms msgServer) CreatePrice(goCtx context.Context, msg *types.MsgCreatePrice } else { logger.Info("final price aggregation done", "feederID", msg.FeederID, "roundID", newItem.PriceTR.RoundID, "price", newItem.PriceTR.Price) } - ms.Keeper.RemoveNonceWithFeederIDForValidators(ctx, msg.FeederID, agc.GetValidators()) decimalStr := strconv.FormatInt(int64(newItem.PriceTR.Decimal), 10) tokenIDStr := strconv.FormatUint(newItem.TokenID, 10) diff --git a/x/oracle/keeper/slashing.go b/x/oracle/keeper/slashing.go new file mode 100644 index 000000000..2a9202ac4 --- /dev/null +++ b/x/oracle/keeper/slashing.go @@ -0,0 +1,153 @@ +package keeper + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/ExocoreNetwork/exocore/x/oracle/types" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + gogotypes "github.com/cosmos/gogoproto/types" +) + +// InitValidatorReportInfo creates a new item for a first-seen validator to track their performance +func (k Keeper) InitValidatorReportInfo(ctx sdk.Context, validator string, height int64) { + store := ctx.KVStore(k.storeKey) + key := types.SlashingValidatorReportInfoKey(validator) + if !store.Has(key) { + // set the record for validator to tracking performance of oracle service + reportInfo := &types.ValidatorReportInfo{ + Address: validator, + StartHeight: height, + } + bz := k.cdc.MustMarshal(reportInfo) + store.Set(key, bz) + } +} + +// SetValidatorReportInfo sets the reporting info for a validator +func (k Keeper) SetValidatorReportInfo(ctx sdk.Context, validator string, info types.ValidatorReportInfo) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&info) + store.Set(types.SlashingValidatorReportInfoKey(validator), bz) +} + +// GetValidatorReportInfo returns the ValidatorReportInfo for a specific validator +func (k Keeper) GetValidatorReportInfo(ctx sdk.Context, validator string) (info types.ValidatorReportInfo, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.SlashingValidatorReportInfoKey(validator)) + if bz == nil { + return + } + k.cdc.MustUnmarshal(bz, &info) + found = true + return +} + +// SetValidatorMissedRoundBitArray sets the bit that checks if the validator has +// missed a round to report price in the current window +func (k Keeper) SetValidatorMissedRoundBitArray(ctx sdk.Context, validator string, index uint64, missed bool) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&gogotypes.BoolValue{Value: missed}) + store.Set(types.SlashingMissedBitArrayKey(validator, index), bz) +} + +// GetValidatorMissedBlocks returns array of missed rounds for given validator +func (k Keeper) GetValidatorMissedRoundBitArray(ctx sdk.Context, validator string, index uint64) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.SlashingMissedBitArrayKey(validator, index)) + if bz == nil { + return false + } + var missed gogotypes.BoolValue + k.cdc.MustUnmarshal(bz, &missed) + return missed.Value +} + +// GetReportedRoundsWindow returns the sliding window size for reporting slashing +func (k Keeper) GetReportedRoundsWindow(ctx sdk.Context) int64 { + return k.GetParams(ctx).Slashing.ReportedRoundsWindow +} + +// GetSlashFractionMiss fraction of power slashed for missed rounds +func (k Keeper) GetSlashFractionMiss(ctx sdk.Context) (res sdk.Dec) { + return k.GetParams(ctx).Slashing.SlashFractionMiss +} + +// GetSlashFractionMalicious fraction returns the fraction of power slashed for malicious behavior +func (k Keeper) GetSlashFractionMalicious(ctx sdk.Context) (res sdk.Dec) { + return k.GetParams(ctx).Slashing.SlashFractionMalicious +} + +// GetMinReportedPerWindow minimum blocks repored prices per window +func (k Keeper) GetMinReportedPerWindow(ctx sdk.Context) int64 { + params := k.GetParams(ctx) + reportedRoundsWindow := k.GetReportedRoundsWindow(ctx) + + // NOTE: RoundInt64 will never panic as minReportedPerWindow is + // less than 1. + return params.Slashing.MinReportedPerWindow.MulInt64(reportedRoundsWindow).RoundInt64() +} + +// GetMissJailDuration returns the jail duration for a validator who misses reports +func (k Keeper) GetMissJailDuration(ctx sdk.Context) (res time.Duration) { + return k.GetParams(ctx).Slashing.OracleMissJailDuration +} + +// GetMaliciousJailDuration returns the jail duration for malicious validator behavior +func (k Keeper) GetMaliciousJailDuration(ctx sdk.Context) (res time.Duration) { + return k.GetParams(ctx).Slashing.OracleMaliciousJailDuration +} + +// IterateValidatorReportedInfos iterates over the stored reportInfo +// and performs a callback function +func (k Keeper) IterateValidatorReportInfos(ctx sdk.Context, handler func(address string, reportInfo types.ValidatorReportInfo) (stop bool)) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.ValidatorReportInfoPrefix) + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + for ; iterator.Valid(); iterator.Next() { + address := string(iterator.Key()) + var info types.ValidatorReportInfo + k.cdc.MustUnmarshal(iterator.Value(), &info) + if handler(address, info) { + break + } + } + iterator.Close() +} + +// IterateValidatorMissedRoundBitArrray iterates all missed rounds in one performance window of rounds +func (k Keeper) IterateValidatorMissedRoundBitArray(ctx sdk.Context, validator string, handler func(index uint64, missed bool) (stop bool)) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.SlashingMissedBitArrayPrefix(validator)) + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + index := binary.BigEndian.Uint64(iterator.Key()) + var missed gogotypes.BoolValue + if err := k.cdc.Unmarshal(iterator.Value(), &missed); err != nil { + panic(fmt.Sprintf("failed to unmarshal missed round: %v", err)) + } + if handler(index, missed.Value) { + break + } + } +} + +func (k Keeper) GetValidatorMissedRounds(ctx sdk.Context, address string) []*types.MissedRound { + missedRounds := []*types.MissedRound{} + k.IterateValidatorMissedRoundBitArray(ctx, address, func(index uint64, missed bool) (stop bool) { + missedRounds = append(missedRounds, types.NewMissedRound(index, missed)) + return false + }) + return missedRounds +} + +// ClearValidatorMissedBlockBitArray deletes every instance of ValidatorMissedBlockBitArray in the store +func (k Keeper) ClearValidatorMissedRoundBitArray(ctx sdk.Context, validator string) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.SlashingMissedBitArrayPrefix(validator)) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } +} diff --git a/x/oracle/module.go b/x/oracle/module.go index 8e089d0b3..6b24de8db 100644 --- a/x/oracle/module.go +++ b/x/oracle/module.go @@ -24,6 +24,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -157,7 +158,12 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { // TODO: try better way to init caches and aggregatorContext than beginBlock once.Do(func() { _ = keeper.GetCaches() - _ = keeper.GetAggregatorContext(ctx, am.keeper) + agc := keeper.GetAggregatorContext(ctx, am.keeper) + validatorPowers := agc.GetValidatorPowers() + // set validatorReportInfo to track performance + for validator := range validatorPowers { + am.keeper.InitValidatorReportInfo(ctx, validator, ctx.BlockHeight()) + } }) } @@ -169,11 +175,17 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val agc := keeper.GetAggregatorContext(ctx, am.keeper) logger := am.keeper.Logger(ctx) + height := ctx.BlockHeight() if len(validatorUpdates) > 0 { validatorList := make(map[string]*big.Int) for _, vu := range validatorUpdates { pubKey, _ := cryptocodec.FromTmProtoPublicKey(vu.PubKey) - validatorList[sdk.ConsAddress(pubKey.Address()).String()] = big.NewInt(vu.Power) + validatorStr := sdk.ConsAddress(pubKey.Address()).String() + validatorList[validatorStr] = big.NewInt(vu.Power) + // add possible new added validator info for slashing tracking + if vu.Power > 0 { + am.keeper.InitValidatorReportInfo(ctx, validatorStr, height) + } } // update validator set information in cache cs.AddCache(cache.ItemV(validatorList)) @@ -183,15 +195,167 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val agc.SetValidatorPowers(validatorPowers) // TODO: seal all alive round since validatorSet changed here forceSeal = true - logger.Info("validator set changed, force seal all active rounds", "height", ctx.BlockHeight()) + logger.Info("validator set changed, force seal all active rounds", "height", height) } // TODO: for v1 use mode==1, just check the failed feeders - _, failed, sealed := agc.SealRound(ctx, forceSeal) + _, failed, sealed, windowClosed := agc.SealRound(ctx, forceSeal) + defer func() { + for _, feederID := range windowClosed { + agc.RemoveWorker(feederID) + am.keeper.RemoveNonceWithFeederIDForValidators(ctx, feederID, agc.GetValidators()) + } + }() + // update&check slashing info + validatorPowers := agc.GetValidatorPowers() + for validator, power := range validatorPowers { + reportedInfo, found := am.keeper.GetValidatorReportInfo(ctx, validator) + if !found { + logger.Error(fmt.Sprintf("Expected report info for validator %s but not found", validator)) + continue + } + // TODO: for the round calculation, now only sourceID=1 is used so {feederID, sourceID} have only one value for each feederID which corresponding to one round. + // But when we came to multiple sources, we should consider the round corresponding to feedeerID instead of {feederID, sourceID} + for _, finalPrice := range agc.GetFinalPriceListForFeederIDs(windowClosed) { + exist, matched := agc.PerformanceReview(ctx, finalPrice, validator) + if exist && !matched { + // TODO: malicious price, just slash&jail immediately + logger.Info( + "confirmed malicious price", + "validator", validator, + "infraction_height", height, + "infraction_time", ctx.BlockTime(), + "feederID", finalPrice.FeederID, + "detID", finalPrice.DetID, + "sourceID", finalPrice.SourceID, + "finalPrice", finalPrice.Price, + ) + consAddr, err := sdk.ConsAddressFromBech32(validator) + if err != nil { + panic("invalid consAddr string") + } + + operator := am.keeper.ValidatorByConsAddr(ctx, consAddr) + if operator != nil && !operator.IsJailed() { + coinsBurned := am.keeper.SlashWithInfractionReason(ctx, consAddr, height, power.Int64(), am.keeper.GetSlashFractionMalicious(ctx), stakingtypes.Infraction_INFRACTION_UNSPECIFIED) + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeOracleSlash, + sdk.NewAttribute(types.AttributeKeyValidatorKey, validator), + sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), + sdk.NewAttribute(types.AttributeKeyReason, types.AttributeValueMaliciousReportPrice), + sdk.NewAttribute(types.AttributeKeyJailed, validator), + sdk.NewAttribute(types.AttributeKeyBurnedCoins, coinsBurned.String()), + ), + ) + am.keeper.Jail(ctx, consAddr) + jailUntil := ctx.BlockHeader().Time.Add(am.keeper.GetMaliciousJailDuration(ctx)) + am.keeper.JailUntil(ctx, consAddr, jailUntil) + reportedInfo.MissedRoundsCounter = 0 + reportedInfo.IndexOffset = 0 + am.keeper.ClearValidatorMissedRoundBitArray(ctx, validator) + } + continue + } + + index := uint64(reportedInfo.IndexOffset % am.keeper.GetReportedRoundsWindow(ctx)) + reportedInfo.IndexOffset++ + // Update reported round bit array & counter + // This counter just tracks the sum of the bit array + // That way we avoid needing to read/write the whole array each time + previous := am.keeper.GetValidatorMissedRoundBitArray(ctx, validator, index) + missed := !exist + switch { + case !previous && missed: + // Array value has changed from not missed to missed, increment counter + am.keeper.SetValidatorMissedRoundBitArray(ctx, validator, index, true) + reportedInfo.MissedRoundsCounter++ + case previous && !missed: + // Array value has changed from missed to not missed, decrement counter + am.keeper.SetValidatorMissedRoundBitArray(ctx, validator, index, false) + reportedInfo.MissedRoundsCounter-- + default: + // Array value at this index has not changed, no need to update counter + } + + minReportedPerWindow := am.keeper.GetMinReportedPerWindow(ctx) + + if missed { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeOracleLiveness, + sdk.NewAttribute(types.AttributeKeyValidatorKey, validator), + sdk.NewAttribute(types.AttributeKeyMissedRounds, fmt.Sprintf("%d", reportedInfo.MissedRoundsCounter)), + sdk.NewAttribute(types.AttributeKeyHeight, fmt.Sprintf("%d", height)), + ), + ) + + logger.Debug( + "absent validator", + "height", ctx.BlockHeight(), + "validator", validator, + "missed", reportedInfo.MissedRoundsCounter, + "threshold", minReportedPerWindow, + ) + } + + minHeight := reportedInfo.StartHeight + am.keeper.GetReportedRoundsWindow(ctx) + maxMissed := am.keeper.GetReportedRoundsWindow(ctx) - minReportedPerWindow + // if we are past the minimum height and the validator has missed too many rounds reporting prices, punish them + if height > minHeight && reportedInfo.MissedRoundsCounter > maxMissed { + consAddr, err := sdk.ConsAddressFromBech32(validator) + if err != nil { + panic("invalid consAddr string") + } + operator := am.keeper.ValidatorByConsAddr(ctx, consAddr) + if operator != nil && !operator.IsJailed() { + // missing rounds confirmed: slash and jail the validator + coinsBurned := am.keeper.SlashWithInfractionReason(ctx, consAddr, height, power.Int64(), am.keeper.GetSlashFractionMiss(ctx), stakingtypes.Infraction_INFRACTION_UNSPECIFIED) + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeOracleSlash, + sdk.NewAttribute(types.AttributeKeyValidatorKey, validator), + sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), + sdk.NewAttribute(types.AttributeKeyReason, types.AttributeValueMissingReportPrice), + sdk.NewAttribute(types.AttributeKeyJailed, validator), + sdk.NewAttribute(types.AttributeKeyBurnedCoins, coinsBurned.String()), + ), + ) + am.keeper.Jail(ctx, consAddr) + jailUntil := ctx.BlockHeader().Time.Add(am.keeper.GetMissJailDuration(ctx)) + am.keeper.JailUntil(ctx, consAddr, jailUntil) + + // We need to reset the counter & array so that the validator won't be immediately slashed for miss report info upon rebonding. + reportedInfo.MissedRoundsCounter = 0 + reportedInfo.IndexOffset = 0 + am.keeper.ClearValidatorMissedRoundBitArray(ctx, validator) + + logger.Info( + "slashing and jailing validator due to liveness fault", + "height", height, + "validator", consAddr.String(), + "min_height", minHeight, + "threshold", minReportedPerWindow, + "slashed", am.keeper.GetSlashFractionMiss(ctx).String(), + "jailed_until", jailUntil, + ) + } else { + // validator was (a) not found or (b) already jailed so we do not slash + logger.Info( + "validator would have been slashed for too many missed repoerting price, but was either not found in store or already jailed", + "validator", validator, + ) + } + } + // Set the updated reportInfo + am.keeper.SetValidatorReportInfo(ctx, validator, reportedInfo) + } + } + for _, feederID := range sealed { am.keeper.RemoveNonceWithFeederIDForValidators(ctx, feederID, agc.GetValidators()) } - // append new round with previous price for fail-seal token + // append new round with previous price for fail-sealed token for _, tokenID := range failed { prevPrice, nextRoundID := am.keeper.GrowRoundID(ctx, tokenID) logger.Info("add new round with previous price under fail aggregation", "tokenID", tokenID, "roundID", nextRoundID, "price", prevPrice) diff --git a/x/oracle/types/events.go b/x/oracle/types/events.go index f8c61132f..3a0d42c2b 100644 --- a/x/oracle/types/events.go +++ b/x/oracle/types/events.go @@ -1,7 +1,9 @@ package types const ( - EventTypeCreatePrice = "create_price" + EventTypeCreatePrice = "create_price" + EventTypeOracleLiveness = "oracle_liveness" + EventTypeOracleSlash = "oracle_slash" AttributeKeyFeederID = "feeder_id" AttributeKeyTokenID = "token_id" @@ -14,10 +16,19 @@ const ( AttributeKeyFeederIDs = "feeder_ids" AttributeKeyNativeTokenUpdate = "native_token_update" AttributeKeyNativeTokenChange = "native_token_change" + AttributeKeyValidatorKey = "validator_key" + AttributeKeyMissedRounds = "missed_rounds" + AttributeKeyHeight = "height" + AttributeKeyPower = "power" + AttributeKeyReason = "reason" + AttributeKeyJailed = "jailed" + AttributeKeyBurnedCoins = "burned_coins" AttributeValuePriceUpdatedSuccess = "success" AttributeValueParamsUpdatedSuccess = "success" AttributeValueNativeTokenUpdate = "update" AttributeValueNativeTokenDeposit = "deposit" AttributeValueNativeTokenWithdraw = "withdraw" + AttributeValueMissingReportPrice = "missing_report_price" + AttributeValueMaliciousReportPrice = "malicious_report_price" ) diff --git a/x/oracle/types/expected_keepers.go b/x/oracle/types/expected_keepers.go index ee9c09b0f..d19ad53d0 100644 --- a/x/oracle/types/expected_keepers.go +++ b/x/oracle/types/expected_keepers.go @@ -1,6 +1,8 @@ package types import ( + time "time" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -26,3 +28,7 @@ type DelegationKeeper interface { type AssetsKeeper interface { GetAssetsDecimal(ctx sdk.Context, assets map[string]interface{}) (decimals map[string]uint32, err error) } + +type SlashingKeeper interface { + JailUntil(sdk.Context, sdk.ConsAddress, time.Time) +} diff --git a/x/oracle/types/genesis.go b/x/oracle/types/genesis.go index d1768b13e..03088bbc8 100644 --- a/x/oracle/types/genesis.go +++ b/x/oracle/types/genesis.go @@ -35,6 +35,13 @@ func NewGenesisState(p Params) *GenesisState { } } +func NewMissedRound(index uint64, missed bool) *MissedRound { + return &MissedRound{ + Index: index, + Missed: missed, + } +} + // Validate performs basic genesis state validation returning an error upon any // failure. func (gs GenesisState) Validate() error { diff --git a/x/oracle/types/genesis.pb.go b/x/oracle/types/genesis.pb.go index dbd1c437f..d3c03a8b4 100644 --- a/x/oracle/types/genesis.pb.go +++ b/x/oracle/types/genesis.pb.go @@ -27,9 +27,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type GenesisState struct { // module params Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - // prices of all tokens + // prices of all tokens including NST PricesList []Prices `protobuf:"bytes,2,rep,name=prices_list,json=pricesList,proto3" json:"prices_list"` //TODO: userDefinedTokenFeeder + // information for memory-cache recovery // latest block on which the validator set be updated ValidatorUpdateBlock *ValidatorUpdateBlock `protobuf:"bytes,3,opt,name=validator_update_block,json=validatorUpdateBlock,proto3" json:"validator_update_block,omitempty"` // index for the cached recent params @@ -40,10 +41,16 @@ type GenesisState struct { RecentMsgList []RecentMsg `protobuf:"bytes,6,rep,name=recent_msg_list,json=recentMsgList,proto3" json:"recent_msg_list"` // cached recent params RecentParamsList []RecentParams `protobuf:"bytes,7,rep,name=recent_params_list,json=recentParamsList,proto3" json:"recent_params_list"` + // information for NST related // stakerInfos for each nst token StakerInfosAssets []StakerInfosAssets `protobuf:"bytes,8,rep,name=staker_infos_assets,json=stakerInfosAssets,proto3" json:"staker_infos_assets"` // stakerList for each nst token StakerListAssets []StakerListAssets `protobuf:"bytes,9,rep,name=staker_list_assets,json=stakerListAssets,proto3" json:"staker_list_assets"` + // information for slashing history + // ValidatorReportInfo records all the validatorReportInfos + ValidatorReportInfos []ValidatorReportInfo `protobuf:"bytes,10,rep,name=validator_report_infos,json=validatorReportInfos,proto3" json:"validator_report_infos"` + // ValidatorMissedRounds records missedRounds for all validators seen + ValidatorMissedRounds []ValidatorMissedRounds `protobuf:"bytes,11,rep,name=validator_missed_rounds,json=validatorMissedRounds,proto3" json:"validator_missed_rounds"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -142,6 +149,20 @@ func (m *GenesisState) GetStakerListAssets() []StakerListAssets { return nil } +func (m *GenesisState) GetValidatorReportInfos() []ValidatorReportInfo { + if m != nil { + return m.ValidatorReportInfos + } + return nil +} + +func (m *GenesisState) GetValidatorMissedRounds() []ValidatorMissedRounds { + if m != nil { + return m.ValidatorMissedRounds + } + return nil +} + // stakerInfosAssets bond stakerinfos to their related assets id type StakerInfosAssets struct { // asset_id tells the assetid which the stakerInfos belong to @@ -252,51 +273,172 @@ func (m *StakerListAssets) GetStakerList() *StakerList { return nil } +// ValidatorMissedRounds record missed rounds indexes for a validator which consAddr corresponding to the address +type ValidatorMissedRounds struct { + // address of validator + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // missed_rounds tells how many rounds this validtor had missed for current windo + MissedRounds []*MissedRound `protobuf:"bytes,2,rep,name=missed_rounds,json=missedRounds,proto3" json:"missed_rounds,omitempty"` +} + +func (m *ValidatorMissedRounds) Reset() { *m = ValidatorMissedRounds{} } +func (m *ValidatorMissedRounds) String() string { return proto.CompactTextString(m) } +func (*ValidatorMissedRounds) ProtoMessage() {} +func (*ValidatorMissedRounds) Descriptor() ([]byte, []int) { + return fileDescriptor_6b68ac5b0c7f4305, []int{3} +} +func (m *ValidatorMissedRounds) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorMissedRounds) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorMissedRounds.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ValidatorMissedRounds) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorMissedRounds.Merge(m, src) +} +func (m *ValidatorMissedRounds) XXX_Size() int { + return m.Size() +} +func (m *ValidatorMissedRounds) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorMissedRounds.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorMissedRounds proto.InternalMessageInfo + +func (m *ValidatorMissedRounds) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ValidatorMissedRounds) GetMissedRounds() []*MissedRound { + if m != nil { + return m.MissedRounds + } + return nil +} + +// MissedRound records if round with index is missed +type MissedRound struct { + // index of the round in current window + Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + // if this round is missed + Missed bool `protobuf:"varint,2,opt,name=missed,proto3" json:"missed,omitempty"` +} + +func (m *MissedRound) Reset() { *m = MissedRound{} } +func (m *MissedRound) String() string { return proto.CompactTextString(m) } +func (*MissedRound) ProtoMessage() {} +func (*MissedRound) Descriptor() ([]byte, []int) { + return fileDescriptor_6b68ac5b0c7f4305, []int{4} +} +func (m *MissedRound) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MissedRound) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MissedRound.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MissedRound) XXX_Merge(src proto.Message) { + xxx_messageInfo_MissedRound.Merge(m, src) +} +func (m *MissedRound) XXX_Size() int { + return m.Size() +} +func (m *MissedRound) XXX_DiscardUnknown() { + xxx_messageInfo_MissedRound.DiscardUnknown(m) +} + +var xxx_messageInfo_MissedRound proto.InternalMessageInfo + +func (m *MissedRound) GetIndex() uint64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *MissedRound) GetMissed() bool { + if m != nil { + return m.Missed + } + return false +} + func init() { proto.RegisterType((*GenesisState)(nil), "exocore.oracle.v1.GenesisState") proto.RegisterType((*StakerInfosAssets)(nil), "exocore.oracle.v1.StakerInfosAssets") proto.RegisterType((*StakerListAssets)(nil), "exocore.oracle.v1.StakerListAssets") + proto.RegisterType((*ValidatorMissedRounds)(nil), "exocore.oracle.v1.ValidatorMissedRounds") + proto.RegisterType((*MissedRound)(nil), "exocore.oracle.v1.MissedRound") } func init() { proto.RegisterFile("exocore/oracle/v1/genesis.proto", fileDescriptor_6b68ac5b0c7f4305) } var fileDescriptor_6b68ac5b0c7f4305 = []byte{ - // 555 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0x5b, 0x6f, 0xd3, 0x30, - 0x14, 0x80, 0xdb, 0xdd, 0xe7, 0x0e, 0x68, 0xbd, 0x09, 0x65, 0x15, 0x4b, 0x47, 0x19, 0x62, 0x12, - 0x52, 0xc2, 0xe0, 0x81, 0x37, 0x34, 0x2a, 0x21, 0xd4, 0x71, 0x11, 0x4a, 0xb9, 0x48, 0x93, 0x50, - 0xe4, 0xa6, 0x26, 0x58, 0x6d, 0xe3, 0xca, 0xf6, 0x4a, 0xf9, 0x17, 0xfc, 0xac, 0x3d, 0xee, 0x91, - 0x27, 0x04, 0xed, 0x1f, 0x41, 0x39, 0x76, 0x58, 0x73, 0x69, 0x79, 0x4b, 0x8e, 0xbf, 0xf3, 0xf9, - 0x9c, 0x63, 0xcb, 0xa8, 0x41, 0x27, 0x3c, 0xe0, 0x82, 0xba, 0x5c, 0x90, 0x60, 0x40, 0xdd, 0xf1, - 0x89, 0x1b, 0xd2, 0x88, 0x4a, 0x26, 0x9d, 0x91, 0xe0, 0x8a, 0xe3, 0x9a, 0x01, 0x1c, 0x0d, 0x38, - 0xe3, 0x93, 0xfa, 0x71, 0x3e, 0x87, 0x45, 0x3d, 0x3a, 0xf1, 0x05, 0x0d, 0x68, 0xa4, 0xfc, 0xa1, - 0x0c, 0x75, 0x72, 0xfd, 0xe1, 0x7f, 0xc8, 0x11, 0x11, 0x64, 0x68, 0x76, 0xaa, 0x1f, 0xe5, 0xe1, - 0x88, 0x28, 0x36, 0xa6, 0xbe, 0xe2, 0x7d, 0x1a, 0x19, 0xca, 0xce, 0x53, 0x29, 0x4b, 0xd1, 0xba, - 0x60, 0x01, 0x4d, 0xd6, 0x9b, 0xf9, 0xf5, 0x5c, 0xd9, 0xf7, 0x17, 0x32, 0xa9, 0xad, 0x9c, 0x3c, - 0x36, 0x26, 0x03, 0xd6, 0x23, 0x8a, 0x0b, 0xff, 0x62, 0xd4, 0x23, 0x8a, 0xfa, 0xdd, 0x01, 0x0f, - 0xfa, 0x86, 0xdf, 0x0b, 0x79, 0xc8, 0xe1, 0xd3, 0x8d, 0xbf, 0x74, 0xb4, 0xf9, 0x67, 0x1d, 0xed, - 0xbc, 0xd4, 0x23, 0xef, 0x28, 0xa2, 0x28, 0x7e, 0x8a, 0x36, 0xf4, 0x36, 0x56, 0xf9, 0xb0, 0x7c, - 0x5c, 0x79, 0xbc, 0xef, 0xe4, 0x8e, 0xc0, 0x79, 0x07, 0x40, 0x6b, 0xed, 0xf2, 0x57, 0xa3, 0xe4, - 0x19, 0x1c, 0x9f, 0xa2, 0x8a, 0x6e, 0xd5, 0x1f, 0x30, 0xa9, 0xac, 0x95, 0xc3, 0xd5, 0x45, 0xd9, - 0x40, 0x99, 0x6c, 0xa4, 0x73, 0x5e, 0x33, 0xa9, 0xf0, 0x67, 0x74, 0xbb, 0xb8, 0x03, 0x6b, 0x15, - 0x4a, 0x79, 0x50, 0x20, 0xfb, 0x98, 0x24, 0x7c, 0x00, 0xbe, 0x15, 0xe3, 0xde, 0xde, 0xb8, 0x20, - 0x8a, 0xdf, 0xa3, 0xdd, 0x82, 0xe3, 0xb7, 0xd6, 0xc0, 0x7d, 0x54, 0xe0, 0x6e, 0xc7, 0xb4, 0x07, - 0xb0, 0xee, 0xd8, 0xab, 0xb1, 0x6c, 0x08, 0xbf, 0x42, 0xd5, 0xec, 0xf5, 0xb3, 0xd6, 0x41, 0x79, - 0x77, 0xb9, 0xf2, 0x8d, 0x0c, 0xbd, 0x9b, 0x2c, 0xf5, 0x8f, 0xcf, 0xd0, 0xad, 0x6b, 0x8d, 0x9e, - 0xe3, 0x06, 0xcc, 0xf1, 0x4e, 0x81, 0xeb, 0x5f, 0x9a, 0x19, 0xe5, 0x0d, 0x91, 0x04, 0x60, 0x9a, - 0x1d, 0x84, 0x53, 0x8d, 0x6a, 0xdd, 0x26, 0xe8, 0x1a, 0x0b, 0x75, 0xa9, 0xa3, 0xad, 0x8a, 0xb9, - 0x18, 0x48, 0xcf, 0xd1, 0xae, 0x54, 0xa4, 0x4f, 0x85, 0xcf, 0xa2, 0x2f, 0x5c, 0xfa, 0x44, 0x4a, - 0xaa, 0xa4, 0xb5, 0x05, 0xd6, 0xa2, 0x19, 0x76, 0x80, 0x6e, 0xc7, 0xf0, 0x73, 0x60, 0x8d, 0xba, - 0x26, 0xb3, 0x0b, 0xf8, 0x13, 0xc2, 0xc6, 0x1d, 0x57, 0x9a, 0xa8, 0xb7, 0x41, 0x7d, 0x6f, 0xa1, - 0x3a, 0x2e, 0x2b, 0x65, 0xae, 0xca, 0x4c, 0xbc, 0x39, 0x42, 0xb5, 0x5c, 0x19, 0x78, 0x1f, 0x6d, - 0xc1, 0x0e, 0x3e, 0xeb, 0xc1, 0x4d, 0xdf, 0xf6, 0x36, 0xe1, 0xbf, 0xdd, 0xc3, 0xa7, 0x68, 0x67, - 0xbe, 0x49, 0x73, 0x95, 0x0f, 0x96, 0x76, 0xe7, 0x55, 0xe6, 0x1a, 0x6a, 0x0e, 0x51, 0x35, 0x5b, - 0xdd, 0xb2, 0x0d, 0x9f, 0xa1, 0xca, 0x5c, 0xe7, 0xd6, 0x0a, 0x5c, 0x9f, 0x83, 0xa5, 0x2d, 0x7b, - 0xe8, 0xba, 0xcd, 0xd6, 0xd9, 0xe5, 0xd4, 0x2e, 0x5f, 0x4d, 0xed, 0xf2, 0xef, 0xa9, 0x5d, 0xfe, - 0x31, 0xb3, 0x4b, 0x57, 0x33, 0xbb, 0xf4, 0x73, 0x66, 0x97, 0xce, 0x1f, 0x85, 0x4c, 0x7d, 0xbd, - 0xe8, 0x3a, 0x01, 0x1f, 0xba, 0x2f, 0xb4, 0xee, 0x2d, 0x55, 0xdf, 0xb8, 0xe8, 0xbb, 0xc9, 0xf3, - 0x31, 0x49, 0x1e, 0x10, 0xf5, 0x7d, 0x44, 0x65, 0x77, 0x03, 0xde, 0x85, 0x27, 0x7f, 0x03, 0x00, - 0x00, 0xff, 0xff, 0x17, 0x12, 0x12, 0xc9, 0x9b, 0x05, 0x00, 0x00, + // 691 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xdd, 0x6e, 0xd3, 0x30, + 0x14, 0xc7, 0xdb, 0x7d, 0xb4, 0xdb, 0xe9, 0x06, 0xad, 0xf7, 0x41, 0x36, 0xb1, 0x6c, 0x94, 0x01, + 0x93, 0x90, 0x52, 0x06, 0x17, 0x5c, 0x20, 0xa1, 0x31, 0x84, 0xd0, 0x06, 0x43, 0x28, 0xe3, 0x43, + 0x9a, 0x84, 0xa2, 0xb4, 0xf1, 0x3a, 0xab, 0x6d, 0x5c, 0xd9, 0x5e, 0x18, 0x6f, 0xc1, 0x8b, 0xf0, + 0x1e, 0xbb, 0xdc, 0x25, 0x57, 0x08, 0xad, 0x2f, 0x82, 0x72, 0x9c, 0xac, 0x49, 0x93, 0x96, 0xbb, + 0xd8, 0xe7, 0x7f, 0x7e, 0xe7, 0xf8, 0x7f, 0xec, 0xc0, 0x26, 0xbd, 0xe0, 0x2d, 0x2e, 0x68, 0x83, + 0x0b, 0xb7, 0xd5, 0xa5, 0x8d, 0x60, 0xb7, 0xd1, 0xa6, 0x3e, 0x95, 0x4c, 0x5a, 0x7d, 0xc1, 0x15, + 0x27, 0xb5, 0x48, 0x60, 0x69, 0x81, 0x15, 0xec, 0xae, 0xef, 0x64, 0x73, 0x98, 0xef, 0xd1, 0x0b, + 0x47, 0xd0, 0x16, 0xf5, 0x95, 0xd3, 0x93, 0x6d, 0x9d, 0xbc, 0xfe, 0xf8, 0x3f, 0xca, 0xbe, 0x2b, + 0xdc, 0x5e, 0x54, 0x69, 0x7d, 0x3b, 0x2b, 0xf6, 0x5d, 0xc5, 0x02, 0xea, 0x28, 0xde, 0xa1, 0x7e, + 0xa4, 0x32, 0xb3, 0xaa, 0x14, 0x25, 0x2f, 0x2e, 0x58, 0x8b, 0xc6, 0xf1, 0x7a, 0x36, 0x9e, 0x69, + 0xfb, 0xc1, 0x58, 0x4d, 0xaa, 0xd4, 0x56, 0x56, 0x26, 0xbb, 0xae, 0x3c, 0x63, 0x7e, 0x0c, 0xb2, + 0xb2, 0x8a, 0xc0, 0xed, 0x32, 0xcf, 0x55, 0x5c, 0x38, 0xe7, 0x7d, 0xcf, 0x55, 0xd4, 0x69, 0x76, + 0x79, 0xab, 0x13, 0xe9, 0x97, 0xdb, 0xbc, 0xcd, 0xf1, 0xb3, 0x11, 0x7e, 0xe9, 0xdd, 0xfa, 0xaf, + 0x32, 0x2c, 0xbc, 0xd5, 0x43, 0x39, 0x56, 0xae, 0xa2, 0xe4, 0x39, 0x94, 0x74, 0x23, 0x46, 0x71, + 0xab, 0xb8, 0x53, 0x79, 0xba, 0x66, 0x65, 0x86, 0x64, 0x7d, 0x44, 0xc1, 0xfe, 0xcc, 0xe5, 0x9f, + 0xcd, 0x82, 0x1d, 0xc9, 0xc9, 0x1e, 0x54, 0xb4, 0x19, 0x4e, 0x97, 0x49, 0x65, 0x4c, 0x6d, 0x4d, + 0x8f, 0xcb, 0x46, 0x55, 0x94, 0x0d, 0x3a, 0xe7, 0x3d, 0x93, 0x8a, 0x7c, 0x83, 0xd5, 0xfc, 0x13, + 0x18, 0xd3, 0xd8, 0xca, 0xa3, 0x1c, 0xd8, 0x97, 0x38, 0xe1, 0x33, 0xea, 0xf7, 0x43, 0xb9, 0xbd, + 0x1c, 0xe4, 0xec, 0x92, 0x4f, 0xb0, 0x94, 0x73, 0x41, 0x8c, 0x19, 0x64, 0x6f, 0xe7, 0xb0, 0x0f, + 0x42, 0xb5, 0x8d, 0x62, 0x7d, 0x62, 0xbb, 0xc6, 0x46, 0xb7, 0xc8, 0x3b, 0xa8, 0x8e, 0x5e, 0x50, + 0x63, 0x16, 0x91, 0xf7, 0x26, 0x23, 0x8f, 0x64, 0xdb, 0xbe, 0xc5, 0x52, 0x6b, 0x72, 0x08, 0xb7, + 0x87, 0x18, 0xed, 0x63, 0x09, 0x7d, 0xbc, 0x9b, 0xc3, 0xba, 0x49, 0x8b, 0xac, 0x5c, 0x14, 0xf1, + 0x06, 0xba, 0x79, 0x0c, 0x24, 0x75, 0x50, 0x8d, 0x2b, 0x23, 0x6e, 0x73, 0x2c, 0x2e, 0x35, 0xda, + 0xaa, 0x48, 0xec, 0x21, 0xf4, 0x04, 0x96, 0xa4, 0x72, 0x3b, 0x54, 0x38, 0xcc, 0x3f, 0xe5, 0xd2, + 0x71, 0xa5, 0xa4, 0x4a, 0x1a, 0x73, 0x48, 0xcd, 0xf3, 0xf0, 0x18, 0xd5, 0x07, 0xa1, 0xf8, 0x15, + 0x6a, 0x23, 0x74, 0x4d, 0x8e, 0x06, 0xc8, 0x57, 0x20, 0x11, 0x3b, 0xec, 0x34, 0x46, 0xcf, 0x23, + 0xfa, 0xfe, 0x58, 0x74, 0xd8, 0x56, 0x8a, 0x5c, 0x95, 0x23, 0xfb, 0xa4, 0x99, 0xbc, 0x57, 0x82, + 0xf6, 0xb9, 0x50, 0xba, 0x7d, 0x03, 0x10, 0xfe, 0x70, 0xd2, 0xbd, 0xb2, 0x51, 0x1f, 0xf6, 0x19, + 0xf1, 0x87, 0x97, 0x6b, 0x18, 0x92, 0xe4, 0x14, 0xee, 0x0c, 0x6b, 0xf4, 0x98, 0x94, 0xd4, 0x73, + 0x04, 0x3f, 0xf7, 0x3d, 0x69, 0x54, 0xb0, 0xc8, 0xce, 0xa4, 0x22, 0x47, 0x98, 0x60, 0xa3, 0x3e, + 0x2a, 0xb3, 0x12, 0xe4, 0x05, 0xeb, 0x7d, 0xa8, 0x65, 0x2c, 0x25, 0x6b, 0x30, 0x87, 0x6e, 0x39, + 0xcc, 0xc3, 0x57, 0x3b, 0x6f, 0x97, 0x71, 0x7d, 0xe0, 0x91, 0x3d, 0x58, 0x48, 0x0e, 0x2c, 0x7a, + 0x96, 0x1b, 0x13, 0x27, 0x65, 0x57, 0x12, 0xc3, 0xa9, 0xf7, 0xa0, 0x3a, 0xea, 0xf4, 0xa4, 0x82, + 0x2f, 0xa1, 0x92, 0x98, 0xa2, 0x31, 0x85, 0x4f, 0x61, 0x63, 0xe2, 0xf8, 0x6c, 0x18, 0x8e, 0xac, + 0x1e, 0xc0, 0x4a, 0xae, 0x2d, 0xc4, 0x80, 0xb2, 0xeb, 0x79, 0x82, 0x4a, 0x79, 0x53, 0x52, 0x2f, + 0xc9, 0x6b, 0x58, 0x4c, 0x3b, 0xae, 0x0f, 0x69, 0xe6, 0x14, 0x4d, 0x10, 0xed, 0x85, 0x5e, 0xd2, + 0xd8, 0x17, 0x50, 0x49, 0x04, 0xc9, 0x32, 0xcc, 0xe2, 0xdb, 0xc4, 0x5a, 0x33, 0xb6, 0x5e, 0x90, + 0x55, 0x28, 0xe9, 0x24, 0x3c, 0xd7, 0x9c, 0x1d, 0xad, 0xf6, 0x0f, 0x2f, 0xaf, 0xcd, 0xe2, 0xd5, + 0xb5, 0x59, 0xfc, 0x7b, 0x6d, 0x16, 0x7f, 0x0e, 0xcc, 0xc2, 0xd5, 0xc0, 0x2c, 0xfc, 0x1e, 0x98, + 0x85, 0x93, 0x27, 0x6d, 0xa6, 0xce, 0xce, 0x9b, 0x56, 0x8b, 0xf7, 0x1a, 0x6f, 0x74, 0x3b, 0x1f, + 0xa8, 0xfa, 0xce, 0x45, 0xa7, 0x11, 0xff, 0xbf, 0x2f, 0xe2, 0x3f, 0xb8, 0xfa, 0xd1, 0xa7, 0xb2, + 0x59, 0xc2, 0x1f, 0xf3, 0xb3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3f, 0xd5, 0x91, 0x78, 0x3e, + 0x07, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -319,6 +461,34 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ValidatorMissedRounds) > 0 { + for iNdEx := len(m.ValidatorMissedRounds) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorMissedRounds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + } + if len(m.ValidatorReportInfos) > 0 { + for iNdEx := len(m.ValidatorReportInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorReportInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + } if len(m.StakerListAssets) > 0 { for iNdEx := len(m.StakerListAssets) - 1; iNdEx >= 0; iNdEx-- { { @@ -524,6 +694,88 @@ func (m *StakerListAssets) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ValidatorMissedRounds) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ValidatorMissedRounds) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorMissedRounds) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MissedRounds) > 0 { + for iNdEx := len(m.MissedRounds) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MissedRounds[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MissedRound) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MissedRound) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MissedRound) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Missed { + i-- + if m.Missed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Index != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { offset -= sovGenesis(v) base := offset @@ -585,6 +837,18 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.ValidatorReportInfos) > 0 { + for _, e := range m.ValidatorReportInfos { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ValidatorMissedRounds) > 0 { + for _, e := range m.ValidatorMissedRounds { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -624,6 +888,40 @@ func (m *StakerListAssets) Size() (n int) { return n } +func (m *ValidatorMissedRounds) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.MissedRounds) > 0 { + for _, e := range m.MissedRounds { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *MissedRound) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovGenesis(uint64(m.Index)) + } + if m.Missed { + n += 2 + } + return n +} + func sovGenesis(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -970,6 +1268,74 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorReportInfos", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorReportInfos = append(m.ValidatorReportInfos, ValidatorReportInfo{}) + if err := m.ValidatorReportInfos[len(m.ValidatorReportInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorMissedRounds", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorMissedRounds = append(m.ValidatorMissedRounds, ValidatorMissedRounds{}) + if err := m.ValidatorMissedRounds[len(m.ValidatorMissedRounds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) @@ -1225,6 +1591,211 @@ func (m *StakerListAssets) Unmarshal(dAtA []byte) error { } return nil } +func (m *ValidatorMissedRounds) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ValidatorMissedRounds: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorMissedRounds: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissedRounds", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MissedRounds = append(m.MissedRounds, &MissedRound{}) + if err := m.MissedRounds[len(m.MissedRounds)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MissedRound) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MissedRound: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MissedRound: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Missed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Missed = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenesis(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/oracle/types/genesis_test.go b/x/oracle/types/genesis_test.go index 76a7843fc..bf73106b9 100644 --- a/x/oracle/types/genesis_test.go +++ b/x/oracle/types/genesis_test.go @@ -2,7 +2,9 @@ package types_test import ( "testing" + "time" + sdkmath "cosmossdk.io/math" "github.com/ExocoreNetwork/exocore/x/oracle/types" "github.com/stretchr/testify/require" ) @@ -55,6 +57,14 @@ func TestGenesisState_Validate(t *testing.T) { Mode: types.ConsensusModeASAP, MaxDetId: 5, MaxSizePrices: 100, + Slashing: &types.SlashingParams{ + ReportedRoundsWindow: 100, + MinReportedPerWindow: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(2)), + OracleMissJailDuration: 600 * time.Second, + OracleMaliciousJailDuration: 30 * 24 * time.Hour, + SlashFractionMiss: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(20)), + SlashFractionMalicious: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(10)), + }, }, // this line is used by starport scaffolding # types/genesis/validField }, @@ -151,7 +161,7 @@ func TestGenesisState_Validate(t *testing.T) { valid: false, }, { - desc: "valid", + desc: "invalid", genState: &types.GenesisState{ StakerListAssets: []types.StakerListAssets{ { diff --git a/x/oracle/types/key_slashing.go b/x/oracle/types/key_slashing.go new file mode 100644 index 000000000..ec5f13ee2 --- /dev/null +++ b/x/oracle/types/key_slashing.go @@ -0,0 +1,20 @@ +package types + +var ( + SlashingPrefix = []byte("Slashing/") + ValidatorReportInfoPrefix = append(SlashingPrefix, []byte("validator/value/")...) + MissedBitArrayPrefix = append(SlashingPrefix, []byte("missed/value/")...) +) + +func SlashingValidatorReportInfoKey(validator string) []byte { + return append(ValidatorReportInfoPrefix, []byte(validator)...) +} + +func SlashingMissedBitArrayPrefix(validator string) []byte { + key := append([]byte(validator), DelimiterForCombinedKey) + return append(MissedBitArrayPrefix, key...) +} + +func SlashingMissedBitArrayKey(validator string, index uint64) []byte { + return append(SlashingMissedBitArrayPrefix(validator), Uint64Bytes(index)...) +} diff --git a/x/oracle/types/params.go b/x/oracle/types/params.go index 71b148610..1e72de69e 100644 --- a/x/oracle/types/params.go +++ b/x/oracle/types/params.go @@ -3,7 +3,9 @@ package types import ( "errors" "strings" + "time" + sdkmath "cosmossdk.io/math" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "gopkg.in/yaml.v2" ) @@ -14,6 +16,7 @@ var ( KeySources = []byte("Sources") KeyRules = []byte("Rules") KeyTokenFeeders = []byte("TokenFeeders") + oneDec = sdkmath.LegacyNewDec(1) ) var _ paramtypes.ParamSet = (*Params)(nil) @@ -103,6 +106,14 @@ func DefaultParams() Params { Mode: ConsensusModeASAP, MaxDetId: 5, MaxSizePrices: 100, + Slashing: &SlashingParams{ + ReportedRoundsWindow: 100, + MinReportedPerWindow: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(2)), + OracleMissJailDuration: 600 * time.Second, + OracleMaliciousJailDuration: 30 * 24 * time.Hour, + SlashFractionMiss: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(20)), + SlashFractionMalicious: sdkmath.LegacyNewDec(1).Quo(sdkmath.LegacyNewDec(10)), + }, } } @@ -129,6 +140,31 @@ func (p Params) Validate() error { return ErrInvalidParams.Wrapf("invalid maxNonce/maxDetID/Threshold/Mode/MaxSizePrices: %d, %d, %d, %d, %d, %d", p.MaxNonce, p.MaxDetId, p.ThresholdA, p.ThresholdB, p.Mode, p.MaxSizePrices) } + slashing := p.Slashing + + if slashing == nil { + return ErrInvalidParams.Wrap("slashing params must not be nil") + } + + if slashing.ReportedRoundsWindow < 1 { + return ErrInvalidParams.Wrapf("ReportedRoundsWindow must be at least 1, got %d", slashing.ReportedRoundsWindow) + } + if slashing.MinReportedPerWindow.GT(oneDec) || !slashing.MinReportedPerWindow.IsPositive() { + return ErrInvalidParams.Wrapf("MinReportedPerWindow must be in (0, 1], got %v", slashing.MinReportedPerWindow) + } + if slashing.SlashFractionMiss.GT(oneDec) || !slashing.SlashFractionMiss.IsPositive() { + return ErrInvalidParams.Wrapf("SlashFractionMiss must be in (0, 1], got %v", slashing.SlashFractionMiss) + } + if slashing.SlashFractionMalicious.GT(oneDec) || !slashing.SlashFractionMalicious.IsPositive() { + return ErrInvalidParams.Wrapf("SlashFractionMalicious must be in (0, 1], got %v", slashing.SlashFractionMalicious) + } + if slashing.OracleMissJailDuration <= 0 { + return ErrInvalidParams.Wrapf("OracleMissJailDuration must be positive, got %v", slashing.OracleMissJailDuration) + } + if slashing.OracleMaliciousJailDuration <= 0 { + return ErrInvalidParams.Wrapf("OracleMaliciousJailDuration must be positive, got %v", slashing.OracleMaliciousJailDuration) + } + // validate tokenFeeders feeders := make(map[uint64]*TokenFeeder) for fID, feeder := range p.TokenFeeders { diff --git a/x/oracle/types/params.pb.go b/x/oracle/types/params.pb.go index ced645ef2..337daabae 100644 --- a/x/oracle/types/params.pb.go +++ b/x/oracle/types/params.pb.go @@ -5,17 +5,23 @@ package types import ( fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/protobuf/types/known/durationpb" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -72,10 +78,12 @@ type Params struct { ThresholdB int32 `protobuf:"varint,8,opt,name=threshold_b,json=thresholdB,proto3" json:"threshold_b,omitempty"` // for v1, mode=1, get final price as soon as voting power reach threshold_a/threshold_b Mode ConsensusMode `protobuf:"varint,9,opt,name=mode,proto3,enum=exocore.oracle.v1.ConsensusMode" json:"mode,omitempty"` - // for each round, a validator only allowed to provide at most max_det_id continuos rounds of prices for DS + // for each round, a validator only allowed to provide at most max_det_id continuous rounds of prices for DS MaxDetId int32 `protobuf:"varint,10,opt,name=max_det_id,json=maxDetId,proto3" json:"max_det_id,omitempty"` // for each token, only keep max_size_prices round of prices MaxSizePrices int32 `protobuf:"varint,11,opt,name=max_size_prices,json=maxSizePrices,proto3" json:"max_size_prices,omitempty"` + // slashing defines the slashing related params + Slashing *SlashingParams `protobuf:"bytes,12,opt,name=slashing,proto3" json:"slashing,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -187,47 +195,143 @@ func (m *Params) GetMaxSizePrices() int32 { return 0 } +func (m *Params) GetSlashing() *SlashingParams { + if m != nil { + return m.Slashing + } + return nil +} + +// slashing related params +type SlashingParams struct { + // reported_rounds_window defines how many rounds included in one window for performance review of missing report + ReportedRoundsWindow int64 `protobuf:"varint,1,opt,name=reported_rounds_window,json=reportedRoundsWindow,proto3" json:"reported_rounds_window,omitempty"` + // min_reported_perwindow defines at least how many rounds should be reported, this is a percentage of window + MinReportedPerWindow github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=min_reported_per_window,json=minReportedPerWindow,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"min_reported_per_window"` + // oracle_miss_jail_duration defines the duration one validator should be jailed for missing reporting price + OracleMissJailDuration time.Duration `protobuf:"bytes,3,opt,name=oracle_miss_jail_duration,json=oracleMissJailDuration,proto3,stdduration" json:"oracle_miss_jail_duration"` + // oracle_malicious_jail_duration defines the duratin one validator should be jailed for malicious behavior + OracleMaliciousJailDuration time.Duration `protobuf:"bytes,4,opt,name=oracle_malicious_jail_duration,json=oracleMaliciousJailDuration,proto3,stdduration" json:"oracle_malicious_jail_duration"` + // slash_fraction_miss defines the fraction one validator should be punished for msissing reporting price + SlashFractionMiss github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=slash_fraction_miss,json=slashFractionMiss,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"slash_fraction_miss"` + // slash_fraction_miss defines the fraction one validator should be punished for malicious behavior + SlashFractionMalicious github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,6,opt,name=slash_fraction_malicious,json=slashFractionMalicious,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"slash_fraction_malicious"` +} + +func (m *SlashingParams) Reset() { *m = SlashingParams{} } +func (m *SlashingParams) String() string { return proto.CompactTextString(m) } +func (*SlashingParams) ProtoMessage() {} +func (*SlashingParams) Descriptor() ([]byte, []int) { + return fileDescriptor_72f39bba4594b794, []int{1} +} +func (m *SlashingParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SlashingParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SlashingParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SlashingParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_SlashingParams.Merge(m, src) +} +func (m *SlashingParams) XXX_Size() int { + return m.Size() +} +func (m *SlashingParams) XXX_DiscardUnknown() { + xxx_messageInfo_SlashingParams.DiscardUnknown(m) +} + +var xxx_messageInfo_SlashingParams proto.InternalMessageInfo + +func (m *SlashingParams) GetReportedRoundsWindow() int64 { + if m != nil { + return m.ReportedRoundsWindow + } + return 0 +} + +func (m *SlashingParams) GetOracleMissJailDuration() time.Duration { + if m != nil { + return m.OracleMissJailDuration + } + return 0 +} + +func (m *SlashingParams) GetOracleMaliciousJailDuration() time.Duration { + if m != nil { + return m.OracleMaliciousJailDuration + } + return 0 +} + func init() { proto.RegisterEnum("exocore.oracle.v1.ConsensusMode", ConsensusMode_name, ConsensusMode_value) proto.RegisterType((*Params)(nil), "exocore.oracle.v1.Params") + proto.RegisterType((*SlashingParams)(nil), "exocore.oracle.v1.SlashingParams") } func init() { proto.RegisterFile("exocore/oracle/v1/params.proto", fileDescriptor_72f39bba4594b794) } var fileDescriptor_72f39bba4594b794 = []byte{ - // 511 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xbf, 0x6f, 0xd3, 0x40, - 0x14, 0xc7, 0x6d, 0xf2, 0xa3, 0xed, 0x85, 0x40, 0x7b, 0x80, 0x74, 0x98, 0xe0, 0x5a, 0x08, 0xa1, - 0x88, 0xc1, 0x6e, 0x1b, 0x26, 0xc4, 0x92, 0x26, 0xa9, 0x14, 0xa4, 0xa6, 0x91, 0x4d, 0x16, 0x16, - 0xcb, 0xb1, 0x5f, 0x12, 0xab, 0xb1, 0x2f, 0xba, 0x73, 0x8a, 0xe9, 0xca, 0x82, 0x3a, 0x31, 0xb2, - 0x54, 0x42, 0xe2, 0x9f, 0x61, 0xec, 0xc8, 0x88, 0x92, 0x7f, 0x83, 0x01, 0xf9, 0xec, 0x94, 0xa6, - 0x49, 0xb7, 0xbb, 0xf7, 0xfd, 0x7c, 0xf4, 0x9e, 0x9e, 0x1e, 0x52, 0x21, 0xa6, 0x2e, 0x65, 0x60, - 0x50, 0xe6, 0xb8, 0x63, 0x30, 0xce, 0xf6, 0x8d, 0x89, 0xc3, 0x9c, 0x80, 0xeb, 0x13, 0x46, 0x23, - 0x8a, 0x77, 0xb2, 0x5c, 0x4f, 0x73, 0xfd, 0x6c, 0x5f, 0xa9, 0xac, 0x2a, 0x7e, 0x38, 0xa0, 0xa9, - 0xa0, 0xbc, 0x5c, 0x4d, 0x23, 0x7a, 0x0a, 0xa1, 0x3d, 0x00, 0xf0, 0x80, 0x65, 0xd4, 0xe3, 0x21, - 0x1d, 0x52, 0xf1, 0x34, 0x92, 0x57, 0x5a, 0x7d, 0xf1, 0x37, 0x87, 0x8a, 0x5d, 0xd1, 0x1d, 0xef, - 0xa1, 0xa2, 0x3b, 0x72, 0xfc, 0x90, 0x13, 0x59, 0xcb, 0x55, 0x4b, 0x07, 0x44, 0x5f, 0x19, 0x44, - 0x6f, 0x24, 0x80, 0x99, 0x71, 0x89, 0x21, 0x1a, 0x71, 0x72, 0xef, 0x4e, 0xe3, 0x43, 0x02, 0x98, - 0x19, 0x87, 0x6b, 0x68, 0x83, 0xd3, 0x29, 0x73, 0x81, 0x93, 0x9c, 0x50, 0x9e, 0xae, 0x51, 0x2c, - 0x41, 0x98, 0x0b, 0x12, 0xd7, 0x50, 0x81, 0x4d, 0xc7, 0xc0, 0x49, 0x5e, 0x28, 0xcf, 0xd7, 0x28, - 0xe6, 0x74, 0x0c, 0x99, 0x96, 0xb2, 0xb8, 0x81, 0xca, 0x37, 0x97, 0xc0, 0x49, 0x41, 0xc8, 0xea, - 0x5d, 0x23, 0x1e, 0x09, 0xcc, 0xbc, 0x1f, 0xfd, 0xff, 0x70, 0xfc, 0x0c, 0x6d, 0x05, 0x4e, 0x6c, - 0x87, 0x34, 0x74, 0x81, 0x14, 0x35, 0xb9, 0x5a, 0x30, 0x37, 0x03, 0x27, 0xee, 0x24, 0x7f, 0xbc, - 0x8b, 0x4a, 0xd1, 0x88, 0x01, 0x1f, 0xd1, 0xb1, 0x67, 0x3b, 0x64, 0x43, 0xc4, 0xe8, 0xba, 0x54, - 0x5f, 0x06, 0xfa, 0x64, 0xf3, 0x16, 0x70, 0x88, 0xdf, 0xa0, 0x7c, 0x40, 0x3d, 0x20, 0x5b, 0x9a, - 0x5c, 0x7d, 0x70, 0xa0, 0xad, 0xdb, 0x37, 0x0d, 0x39, 0x84, 0x7c, 0xca, 0x8f, 0xa9, 0x07, 0xa6, - 0xa0, 0x71, 0x05, 0xa1, 0x64, 0x28, 0x0f, 0x22, 0xdb, 0xf7, 0x08, 0xba, 0x9e, 0xaa, 0x09, 0x51, - 0xdb, 0xc3, 0xaf, 0xd0, 0xc3, 0x24, 0xe5, 0xfe, 0x39, 0xd8, 0x13, 0xe6, 0x27, 0x9b, 0x2e, 0x09, - 0xa4, 0x1c, 0x38, 0xb1, 0xe5, 0x9f, 0x43, 0x57, 0x14, 0xdf, 0xe6, 0xbf, 0xff, 0xd8, 0x95, 0x5e, - 0x7f, 0x91, 0x51, 0x79, 0xa9, 0x07, 0x7e, 0x87, 0x94, 0xc6, 0x49, 0xc7, 0x6a, 0x75, 0xac, 0x9e, - 0x65, 0x1f, 0x9f, 0x34, 0x5b, 0x76, 0xaf, 0x63, 0x75, 0x5b, 0x8d, 0xf6, 0x51, 0xbb, 0xd5, 0xdc, - 0x96, 0x94, 0xca, 0xc5, 0xa5, 0x46, 0x96, 0x94, 0x5e, 0xc8, 0x27, 0xe0, 0xfa, 0x03, 0x1f, 0x3c, - 0xac, 0xa3, 0x47, 0xb7, 0xec, 0xba, 0x55, 0xef, 0x6e, 0xcb, 0xca, 0x93, 0x8b, 0x4b, 0x6d, 0x67, - 0x49, 0x4b, 0x02, 0x25, 0xff, 0xf5, 0xa7, 0x2a, 0x1d, 0xbe, 0xff, 0x35, 0x53, 0xe5, 0xab, 0x99, - 0x2a, 0xff, 0x99, 0xa9, 0xf2, 0xb7, 0xb9, 0x2a, 0x5d, 0xcd, 0x55, 0xe9, 0xf7, 0x5c, 0x95, 0x3e, - 0xee, 0x0d, 0xfd, 0x68, 0x34, 0xed, 0xeb, 0x2e, 0x0d, 0x8c, 0x56, 0xba, 0x9d, 0x0e, 0x44, 0x9f, - 0x28, 0x3b, 0x35, 0x16, 0x47, 0x1f, 0x2f, 0xce, 0x3e, 0xfa, 0x3c, 0x01, 0xde, 0x2f, 0x8a, 0xbb, - 0xae, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xf5, 0xbe, 0x10, 0x4f, 0x66, 0x03, 0x00, 0x00, + // 791 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x4d, 0x4f, 0xeb, 0x46, + 0x14, 0x8d, 0x5f, 0x3e, 0x1e, 0x6f, 0x42, 0x5e, 0x5f, 0x06, 0x4a, 0x87, 0x40, 0x1d, 0x17, 0x55, + 0x28, 0x42, 0xaa, 0x0d, 0x84, 0x55, 0xd5, 0x2e, 0xc8, 0x07, 0x12, 0x48, 0x84, 0xc8, 0x29, 0xaa, + 0xd4, 0x8d, 0xe5, 0xd8, 0x93, 0x64, 0xc0, 0xf6, 0x44, 0x33, 0x36, 0xa4, 0x6c, 0xbb, 0xa9, 0x58, + 0x75, 0xc9, 0x06, 0xa9, 0x52, 0x37, 0x5d, 0x76, 0xdf, 0x3f, 0xc0, 0x92, 0x65, 0xc5, 0x82, 0x56, + 0xb0, 0xe8, 0xdf, 0xa8, 0x3c, 0xb6, 0x53, 0x12, 0xc2, 0xe2, 0xb1, 0x49, 0x3c, 0xf7, 0x9c, 0x73, + 0xcf, 0xbd, 0x77, 0x7c, 0x0d, 0x64, 0x3c, 0xa2, 0x16, 0x65, 0x58, 0xa3, 0xcc, 0xb4, 0x1c, 0xac, + 0x9d, 0x6d, 0x69, 0x43, 0x93, 0x99, 0x2e, 0x57, 0x87, 0x8c, 0xfa, 0x14, 0x16, 0x63, 0x5c, 0x8d, + 0x70, 0xf5, 0x6c, 0xab, 0x54, 0x34, 0x5d, 0xe2, 0x51, 0x4d, 0xfc, 0x46, 0xac, 0xd2, 0xea, 0xf3, + 0x2c, 0xc4, 0xeb, 0x25, 0xe8, 0x97, 0xcf, 0x51, 0x9f, 0x9e, 0x62, 0xcf, 0xe8, 0x61, 0x6c, 0x63, + 0x16, 0xb3, 0x16, 0xfb, 0xb4, 0x4f, 0xc5, 0xa3, 0x16, 0x3e, 0xc5, 0x51, 0xb9, 0x4f, 0x69, 0xdf, + 0xc1, 0x9a, 0x38, 0x75, 0x83, 0x9e, 0x66, 0x07, 0xcc, 0xf4, 0x09, 0xf5, 0x22, 0x7c, 0xed, 0xcf, + 0x0c, 0xc8, 0xb5, 0x45, 0xc1, 0x70, 0x13, 0xe4, 0xac, 0x81, 0x49, 0x3c, 0x8e, 0x24, 0x25, 0x5d, + 0xc9, 0x6f, 0x23, 0xf5, 0x59, 0xed, 0x6a, 0x3d, 0x24, 0xe8, 0x31, 0x2f, 0x54, 0x88, 0x42, 0x38, + 0x7a, 0xf3, 0xa2, 0xe2, 0xbb, 0x90, 0xa0, 0xc7, 0x3c, 0x58, 0x05, 0x6f, 0x39, 0x0d, 0x98, 0x85, + 0x39, 0x4a, 0x0b, 0xc9, 0xf2, 0x0c, 0x49, 0x47, 0x30, 0xf4, 0x84, 0x09, 0xab, 0x20, 0xcb, 0x02, + 0x07, 0x73, 0x94, 0x11, 0x92, 0xcf, 0x67, 0x48, 0xf4, 0xc0, 0xc1, 0xb1, 0x2c, 0xe2, 0xc2, 0x3a, + 0x28, 0x3c, 0x1d, 0x12, 0x47, 0x59, 0x21, 0x96, 0x5f, 0x2a, 0x71, 0x4f, 0xd0, 0xf4, 0x79, 0xff, + 0xff, 0x03, 0x87, 0x2b, 0xe0, 0x9d, 0x6b, 0x8e, 0x0c, 0x8f, 0x7a, 0x16, 0x46, 0x39, 0x45, 0xaa, + 0x64, 0xf5, 0x39, 0xd7, 0x1c, 0xb5, 0xc2, 0x33, 0x2c, 0x83, 0xbc, 0x3f, 0x60, 0x98, 0x0f, 0xa8, + 0x63, 0x1b, 0x26, 0x7a, 0x2b, 0x60, 0x30, 0x0e, 0xed, 0x4e, 0x12, 0xba, 0x68, 0x6e, 0x8a, 0x50, + 0x83, 0x3b, 0x20, 0xe3, 0x52, 0x1b, 0xa3, 0x77, 0x8a, 0x54, 0x79, 0xbf, 0xad, 0xcc, 0x9a, 0x37, + 0xf5, 0x38, 0xf6, 0x78, 0xc0, 0x0f, 0xa9, 0x8d, 0x75, 0xc1, 0x86, 0xab, 0x00, 0x84, 0x45, 0xd9, + 0xd8, 0x37, 0x88, 0x8d, 0xc0, 0xb8, 0xaa, 0x06, 0xf6, 0xf7, 0x6d, 0xb8, 0x0e, 0x3e, 0x09, 0x51, + 0x4e, 0x2e, 0xb0, 0x31, 0x64, 0x24, 0x9c, 0x74, 0x5e, 0x50, 0x0a, 0xae, 0x39, 0xea, 0x90, 0x0b, + 0xdc, 0x16, 0x41, 0xf8, 0x2d, 0x98, 0xe3, 0x8e, 0xc9, 0x07, 0xc4, 0xeb, 0xa3, 0x79, 0x45, 0xaa, + 0xe4, 0xb7, 0xbf, 0x98, 0x75, 0x15, 0x31, 0x25, 0x7a, 0x45, 0xf4, 0xb1, 0xe4, 0xeb, 0xcc, 0xd5, + 0xaf, 0xe5, 0xd4, 0xda, 0x5d, 0x06, 0xbc, 0x9f, 0xa4, 0xc0, 0x1d, 0xb0, 0xc4, 0xf0, 0x90, 0x32, + 0x1f, 0xdb, 0x06, 0xa3, 0x81, 0x67, 0x73, 0xe3, 0x9c, 0x78, 0x36, 0x3d, 0x47, 0x92, 0x22, 0x55, + 0xd2, 0xfa, 0x62, 0x82, 0xea, 0x02, 0xfc, 0x5e, 0x60, 0xf0, 0x04, 0x7c, 0xe6, 0x12, 0xcf, 0x18, + 0x2b, 0x87, 0x98, 0x25, 0xb2, 0x37, 0x8a, 0x54, 0x99, 0xaf, 0x55, 0x6f, 0xee, 0xcb, 0xa9, 0xbb, + 0xfb, 0xf2, 0x7a, 0x9f, 0xf8, 0x83, 0xa0, 0xab, 0x5a, 0xd4, 0xd5, 0x2c, 0xca, 0x5d, 0xca, 0xe3, + 0xbf, 0xaf, 0xb8, 0x7d, 0xaa, 0xf9, 0x3f, 0x0e, 0x31, 0x57, 0x1b, 0xd8, 0xfa, 0xfd, 0xdf, 0x3f, + 0x36, 0x24, 0x7d, 0xd1, 0x25, 0x9e, 0x1e, 0xa7, 0x6c, 0x63, 0x16, 0x7b, 0x59, 0x60, 0x39, 0x6a, + 0xd0, 0x70, 0x09, 0xe7, 0xc6, 0x89, 0x49, 0x1c, 0x23, 0xd9, 0x0a, 0x94, 0x16, 0xa3, 0x58, 0x56, + 0xa3, 0xb5, 0x51, 0x93, 0xb5, 0x51, 0x1b, 0x31, 0xa1, 0x56, 0x08, 0x0b, 0xb9, 0xfa, 0xbb, 0x2c, + 0x45, 0x16, 0x4b, 0x51, 0xaa, 0x43, 0xc2, 0xf9, 0x81, 0x49, 0x9c, 0x84, 0x06, 0x5d, 0x20, 0x27, + 0x26, 0xa6, 0x43, 0x2c, 0x42, 0x83, 0x69, 0xa7, 0xcc, 0x47, 0x3a, 0xad, 0xc4, 0x4e, 0x49, 0xba, + 0x09, 0x3b, 0x0b, 0x2c, 0x88, 0xab, 0x31, 0x7a, 0xcc, 0xb4, 0xc2, 0x88, 0xe8, 0x0d, 0x65, 0x5f, + 0x3f, 0xbb, 0xa2, 0xc8, 0xb7, 0x17, 0xa7, 0x0b, 0xfb, 0x83, 0x2e, 0x40, 0xd3, 0x26, 0x49, 0x31, + 0x62, 0x39, 0x5e, 0xe9, 0xb4, 0x34, 0xe9, 0x94, 0xa4, 0xdc, 0xf8, 0x49, 0x02, 0x85, 0x89, 0xf7, + 0x1f, 0x7e, 0x03, 0x4a, 0xf5, 0xa3, 0x56, 0xa7, 0xd9, 0xea, 0x1c, 0x77, 0x8c, 0xc3, 0xa3, 0x46, + 0xd3, 0x38, 0x6e, 0x75, 0xda, 0xcd, 0xfa, 0xfe, 0xde, 0x7e, 0xb3, 0xf1, 0x21, 0x55, 0x5a, 0xbd, + 0xbc, 0x56, 0xd0, 0x84, 0xe4, 0xd8, 0xe3, 0x43, 0x6c, 0x91, 0x1e, 0xc1, 0x36, 0x54, 0xc1, 0xc2, + 0x94, 0x7a, 0xb7, 0xb3, 0xdb, 0xfe, 0x20, 0x95, 0x3e, 0xbd, 0xbc, 0x56, 0x8a, 0x13, 0xb2, 0x10, + 0x28, 0x65, 0x7e, 0xfe, 0x4d, 0x4e, 0xd5, 0x0e, 0x6e, 0x1e, 0x64, 0xe9, 0xf6, 0x41, 0x96, 0xfe, + 0x79, 0x90, 0xa5, 0x5f, 0x1e, 0xe5, 0xd4, 0xed, 0xa3, 0x9c, 0xfa, 0xeb, 0x51, 0x4e, 0xfd, 0xb0, + 0xf9, 0xa4, 0xc9, 0x66, 0xb4, 0x39, 0x2d, 0xec, 0x9f, 0x53, 0x76, 0xaa, 0x25, 0x1f, 0xec, 0x51, + 0xf2, 0xc9, 0x16, 0x2d, 0x77, 0x73, 0xe2, 0x92, 0xab, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x58, + 0x98, 0x95, 0x8e, 0x35, 0x06, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -250,6 +354,18 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Slashing != nil { + { + size, err := m.Slashing.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } if m.MaxSizePrices != 0 { i = encodeVarintParams(dAtA, i, uint64(m.MaxSizePrices)) i-- @@ -353,6 +469,80 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SlashingParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SlashingParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SlashingParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.SlashFractionMalicious.Size() + i -= size + if _, err := m.SlashFractionMalicious.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + { + size := m.SlashFractionMiss.Size() + i -= size + if _, err := m.SlashFractionMiss.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + n2, err2 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.OracleMaliciousJailDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.OracleMaliciousJailDuration):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintParams(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x22 + n3, err3 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.OracleMissJailDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.OracleMissJailDuration):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintParams(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x1a + { + size := m.MinReportedPerWindow.Size() + i -= size + if _, err := m.MinReportedPerWindow.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.ReportedRoundsWindow != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.ReportedRoundsWindow)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintParams(dAtA []byte, offset int, v uint64) int { offset -= sovParams(v) base := offset @@ -418,6 +608,32 @@ func (m *Params) Size() (n int) { if m.MaxSizePrices != 0 { n += 1 + sovParams(uint64(m.MaxSizePrices)) } + if m.Slashing != nil { + l = m.Slashing.Size() + n += 1 + l + sovParams(uint64(l)) + } + return n +} + +func (m *SlashingParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReportedRoundsWindow != 0 { + n += 1 + sovParams(uint64(m.ReportedRoundsWindow)) + } + l = m.MinReportedPerWindow.Size() + n += 1 + l + sovParams(uint64(l)) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.OracleMissJailDuration) + n += 1 + l + sovParams(uint64(l)) + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.OracleMaliciousJailDuration) + n += 1 + l + sovParams(uint64(l)) + l = m.SlashFractionMiss.Size() + n += 1 + l + sovParams(uint64(l)) + l = m.SlashFractionMalicious.Size() + n += 1 + l + sovParams(uint64(l)) return n } @@ -740,6 +956,276 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Slashing", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Slashing == nil { + m.Slashing = &SlashingParams{} + } + if err := m.Slashing.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SlashingParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SlashingParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SlashingParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReportedRoundsWindow", wireType) + } + m.ReportedRoundsWindow = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReportedRoundsWindow |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinReportedPerWindow", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinReportedPerWindow.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OracleMissJailDuration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.OracleMissJailDuration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OracleMaliciousJailDuration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.OracleMaliciousJailDuration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashFractionMiss", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SlashFractionMiss.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashFractionMalicious", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SlashFractionMalicious.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/oracle/types/slashing.pb.go b/x/oracle/types/slashing.pb.go new file mode 100644 index 000000000..31dfc7395 --- /dev/null +++ b/x/oracle/types/slashing.pb.go @@ -0,0 +1,431 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: exocore/oracle/v1/slashing.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ValidatorReportInfo represents the information to describe the miss status of a validator reporting prices +type ValidatorReportInfo struct { + // address of the validtor + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // start_height for the performance round of the configured window of rounds + StartHeight int64 `protobuf:"varint,2,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"` + // index_offset track the offset of current window + IndexOffset int64 `protobuf:"varint,3,opt,name=index_offset,json=indexOffset,proto3" json:"index_offset,omitempty"` + // missed_rounds_counter counts the number of missed rounds for this window + MissedRoundsCounter int64 `protobuf:"varint,4,opt,name=missed_rounds_counter,json=missedRoundsCounter,proto3" json:"missed_rounds_counter,omitempty"` +} + +func (m *ValidatorReportInfo) Reset() { *m = ValidatorReportInfo{} } +func (m *ValidatorReportInfo) String() string { return proto.CompactTextString(m) } +func (*ValidatorReportInfo) ProtoMessage() {} +func (*ValidatorReportInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_27165252f41a09d2, []int{0} +} +func (m *ValidatorReportInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorReportInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorReportInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ValidatorReportInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorReportInfo.Merge(m, src) +} +func (m *ValidatorReportInfo) XXX_Size() int { + return m.Size() +} +func (m *ValidatorReportInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorReportInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorReportInfo proto.InternalMessageInfo + +func (m *ValidatorReportInfo) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ValidatorReportInfo) GetStartHeight() int64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +func (m *ValidatorReportInfo) GetIndexOffset() int64 { + if m != nil { + return m.IndexOffset + } + return 0 +} + +func (m *ValidatorReportInfo) GetMissedRoundsCounter() int64 { + if m != nil { + return m.MissedRoundsCounter + } + return 0 +} + +func init() { + proto.RegisterType((*ValidatorReportInfo)(nil), "exocore.oracle.v1.ValidatorReportInfo") +} + +func init() { proto.RegisterFile("exocore/oracle/v1/slashing.proto", fileDescriptor_27165252f41a09d2) } + +var fileDescriptor_27165252f41a09d2 = []byte{ + // 260 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0x90, 0x31, 0x4e, 0x84, 0x40, + 0x14, 0x86, 0x19, 0xd7, 0x68, 0x44, 0x1b, 0xd9, 0x98, 0x50, 0x4d, 0xd0, 0x6a, 0x2b, 0x70, 0xf5, + 0x06, 0x1a, 0x13, 0xb5, 0xd0, 0x84, 0xc2, 0xc2, 0x86, 0xcc, 0x32, 0x0f, 0x98, 0xc8, 0xf2, 0xc8, + 0xcc, 0x63, 0xc5, 0x5b, 0x78, 0x0d, 0x6f, 0x62, 0xb9, 0xa5, 0xa5, 0x81, 0x8b, 0x18, 0x67, 0x96, + 0xf2, 0x7d, 0xff, 0xd7, 0xbc, 0xcf, 0x8f, 0xa0, 0xc7, 0x1c, 0x35, 0x24, 0xa8, 0x45, 0x5e, 0x43, + 0xb2, 0x59, 0x26, 0xa6, 0x16, 0xa6, 0x52, 0x4d, 0x19, 0xb7, 0x1a, 0x09, 0x83, 0xd3, 0x9d, 0x11, + 0x3b, 0x23, 0xde, 0x2c, 0x2f, 0xbe, 0x98, 0x3f, 0x7f, 0x11, 0xb5, 0x92, 0x82, 0x50, 0xa7, 0xd0, + 0xa2, 0xa6, 0x87, 0xa6, 0xc0, 0x20, 0xf4, 0x0f, 0x85, 0x94, 0x1a, 0x8c, 0x09, 0x59, 0xc4, 0x16, + 0x47, 0xe9, 0x74, 0x06, 0xe7, 0xfe, 0x89, 0x21, 0xa1, 0x29, 0xab, 0x40, 0x95, 0x15, 0x85, 0x7b, + 0x11, 0x5b, 0xcc, 0xd2, 0x63, 0xcb, 0xee, 0x2d, 0xfa, 0x57, 0x54, 0x23, 0xa1, 0xcf, 0xb0, 0x28, + 0x0c, 0x50, 0x38, 0x73, 0x8a, 0x65, 0xcf, 0x16, 0x05, 0x57, 0xfe, 0xd9, 0x5a, 0x19, 0x03, 0x32, + 0xd3, 0xd8, 0x35, 0xd2, 0x64, 0x39, 0x76, 0x0d, 0x81, 0x0e, 0xf7, 0xad, 0x3b, 0x77, 0x63, 0x6a, + 0xb7, 0x5b, 0x37, 0xdd, 0x3c, 0x7e, 0x0f, 0x9c, 0x6d, 0x07, 0xce, 0x7e, 0x07, 0xce, 0x3e, 0x47, + 0xee, 0x6d, 0x47, 0xee, 0xfd, 0x8c, 0xdc, 0x7b, 0xbd, 0x2c, 0x15, 0x55, 0xdd, 0x2a, 0xce, 0x71, + 0x9d, 0xdc, 0xb9, 0x1f, 0x9f, 0x80, 0xde, 0x51, 0xbf, 0x25, 0x53, 0x94, 0x7e, 0xca, 0x42, 0x1f, + 0x2d, 0x98, 0xd5, 0x81, 0x2d, 0x72, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xfc, 0xb6, 0xd0, 0xba, + 0x35, 0x01, 0x00, 0x00, +} + +func (m *ValidatorReportInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ValidatorReportInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorReportInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MissedRoundsCounter != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.MissedRoundsCounter)) + i-- + dAtA[i] = 0x20 + } + if m.IndexOffset != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.IndexOffset)) + i-- + dAtA[i] = 0x18 + } + if m.StartHeight != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintSlashing(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSlashing(dAtA []byte, offset int, v uint64) int { + offset -= sovSlashing(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ValidatorReportInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovSlashing(uint64(l)) + } + if m.StartHeight != 0 { + n += 1 + sovSlashing(uint64(m.StartHeight)) + } + if m.IndexOffset != 0 { + n += 1 + sovSlashing(uint64(m.IndexOffset)) + } + if m.MissedRoundsCounter != 0 { + n += 1 + sovSlashing(uint64(m.MissedRoundsCounter)) + } + return n +} + +func sovSlashing(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSlashing(x uint64) (n int) { + return sovSlashing(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ValidatorReportInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ValidatorReportInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorReportInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexOffset", wireType) + } + m.IndexOffset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IndexOffset |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MissedRoundsCounter", wireType) + } + m.MissedRoundsCounter = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MissedRoundsCounter |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSlashing(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSlashing + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSlashing(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlashing + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlashing + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlashing + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSlashing + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSlashing + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSlashing + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSlashing = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSlashing = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSlashing = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/oracle/types/types.go b/x/oracle/types/types.go index 18135f518..09365b51a 100644 --- a/x/oracle/types/types.go +++ b/x/oracle/types/types.go @@ -36,11 +36,20 @@ type Price struct { Decimal uint8 } +type AggFinalPrice struct { + FeederID uint64 + SourceID uint64 + DetID string + Price string +} + const ( DefaultPriceValue = 1 DefaultPriceDecimal = 0 ) +var DelimiterForCombinedKey = byte('/') + func Uint64Bytes(value uint64) []byte { valueBytes := make([]byte, 8) binary.BigEndian.PutUint64(valueBytes, value)