diff --git a/proto/buf.lock b/proto/buf.lock index face468fb..d025fc2f3 100644 --- a/proto/buf.lock +++ b/proto/buf.lock @@ -5,15 +5,19 @@ deps: owner: cosmos repository: cosmos-proto commit: 1935555c206d4afb9e94615dfd0fad31 + digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377 - remote: buf.build owner: cosmos repository: cosmos-sdk - commit: 508e19f5f37549e3a471a2a59b903c00 + commit: 954f7b05f38440fc8250134b15adec47 + digest: shake256:2ab4404fd04a7d1d52df0e2d0f2d477a3d83ffd88d876957bf3fedfd702c8e52833d65b3ce1d89a3c5adf2aab512616b0e4f51d8463f07eda9a8a3317ee3ac54 - remote: buf.build owner: cosmos repository: gogo-proto - commit: 34d970b699f84aa382f3c29773a60836 + commit: 88ef6483f90f478fb938c37dde52ece3 + digest: shake256:89c45df2aa11e0cff97b0d695436713db3d993d76792e9f8dc1ae90e6ab9a9bec55503d48ceedd6b86069ab07d3041b32001b2bfe0227fa725dd515ff381e5ba - remote: buf.build owner: googleapis repository: googleapis - commit: 783e4b5374fa488ab068d08af9658438 + commit: e874a0be2bf140a5a4c7d4122c635823 + digest: shake256:4fe3036b4d706f6ee2b13c730bd04777f021dfd02ed27e6e40480acfe664a7548238312ee0727fd77648a38d227e296d43f4a38a34cdd46068156211016d9657 diff --git a/proto/buf.yaml b/proto/buf.yaml index d7876a9fc..00eff3659 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -1,7 +1,7 @@ version: v1 name: buf.build/evmos/evmos deps: - - buf.build/cosmos/cosmos-sdk + - buf.build/cosmos/cosmos-sdk:v0.47.0 - buf.build/cosmos/cosmos-proto - buf.build/cosmos/gogo-proto - buf.build/googleapis/googleapis diff --git a/proto/exocore/dogfood/v1/dogfood.proto b/proto/exocore/dogfood/v1/dogfood.proto new file mode 100644 index 000000000..21c5b350b --- /dev/null +++ b/proto/exocore/dogfood/v1/dogfood.proto @@ -0,0 +1,108 @@ +syntax = "proto3"; + +package exocore.dogfood.v1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +import "cosmos_proto/cosmos.proto"; +import "tendermint/crypto/keys.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option go_package = "github.com/ExocoreNetwork/exocore/x/dogfood/types"; + +// ExocoreValidator is a validator that is part of the Exocore network. It is +// used to validate and sign blocks and transactions. +message ExocoreValidator { + // The address, as derived from the consensus key. It has no relation + // with the operator's account address. + bytes address = 1; + // Last known power + int64 power = 2; + // pubkey is the consensus public key of the validator, as a Protobuf Any. + google.protobuf.Any pubkey = 3 [ + (cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", + (gogoproto.moretags) = "yaml:\"consensus_pubkey\"" + ]; +} + +// OperationType is used to indicate the type of operation that is being +// cached by the module to create the updated validator set. +enum OperationType { + option (gogoproto.goproto_enum_prefix) = false; + // KeyOpUnspecified is used to indicate that the operation type is not specified. + // This should never be used. + OPERATION_TYPE_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "KeyOpUnspecified" ]; + // KeyAddition is used to indicate that the operation is a key addition. + OPERATION_TYPE_ADDITION_OR_UPDATE = 1 [ (gogoproto.enumvalue_customname) = "KeyAdditionOrUpdate" ]; + // KeyRemoval is used to indicate that the operation is a key removal. Typically + // this is done due to key replacement mechanism and not directly. + OPERATION_TYPE_REMOVAL = 2 [ (gogoproto.enumvalue_customname) = "KeyRemoval" ]; +} + +// QueueResultType is used to indicate the result of the queue operation. +enum QueueResultType { + option (gogoproto.goproto_enum_prefix) = false; + // QueueResultUnspecified is used to indicate that the queue result type is not specified. + QUEUE_RESULT_TYPE_UNSPECIFIED = 0 [ (gogoproto.enumvalue_customname) = "QueueResultUnspecified" ]; + // QueueResultSuccess is used to indicate that the queue operation was successful. + QUEUE_RESULT_TYPE_SUCCESS = 1 [ (gogoproto.enumvalue_customname) = "QueueResultSuccess" ]; + // QueueResultExists is used to indicate that the queue operation failed because the + // operation already exists in the queue. + QUEUE_RESULT_TYPE_EXISTS = 2 [ (gogoproto.enumvalue_customname) = "QueueResultExists" ]; + // QueueResultRemoved is used to indicate that the queue operation resulted in an existing + // operation being removed from the queue. + QUEUE_RESULT_TYPE_REMOVED = 3 [ (gogoproto.enumvalue_customname) = "QueueResultRemoved" ]; +} + +// Operation is used to indicate the operation that is being cached by the module +// to create the updated validator set. +message Operation { + // OperationType is the type of the operation (addition / removal). + OperationType operation_type = 1; + // OperatorAddress is the sdk.AccAddress of the operator. + bytes operator_address = 2; + // PubKey is the public key for which the operation is being applied. + tendermint.crypto.PublicKey pub_key = 3 [(gogoproto.nullable) = false]; +} + +// Operations is a collection of Operation. +message Operations { + repeated Operation list = 1 [(gogoproto.nullable) = false]; +} + +// AccountAddresses represents a list of account addresses. It is used to store the list of +// operator addresses whose operations are maturing at an epoch. +message AccountAddresses { + repeated bytes list = 1; +} + +// ConsensusAddresses represents a list of account addresses. It is used to store the list of +// addresses (which correspond to operator public keys) to delete at the end of an epoch. +message ConsensusAddresses { + repeated bytes list = 1; +} + +// UndelegationRecordKeys is a collection of undelegation record keys. This is used to store a +// list of undelegation records to mature in the delegation module at the end of the epoch. +message UndelegationRecordKeys { + repeated bytes list = 1; +} + +// Validators is a list of validators stored according to the staking module. +message Validators { + repeated cosmos.staking.v1beta1.Validator list = 1 [(gogoproto.nullable) = false]; +} + +// HeaderSubset is a subset of the block header that is relevant to the IBC codebase. It is +// stored for each height and then converted to the `tm.Header` object after queried. It is +// pruned when the information is no longer needed according to the `HistoricalEntries` param. +message HeaderSubset { + // timestamp of the block + google.protobuf.Timestamp time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // validators for the next block + bytes next_validators_hash = 2; + // state after txs from the previous block + bytes app_hash = 3; +} \ No newline at end of file diff --git a/proto/exocore/dogfood/v1/genesis.proto b/proto/exocore/dogfood/v1/genesis.proto index 4cb6bca2f..02b53d840 100644 --- a/proto/exocore/dogfood/v1/genesis.proto +++ b/proto/exocore/dogfood/v1/genesis.proto @@ -3,11 +3,19 @@ syntax = "proto3"; package exocore.dogfood.v1; import "gogoproto/gogo.proto"; + +import "tendermint/abci/types.proto"; + import "exocore/dogfood/v1/params.proto"; option go_package = "github.com/ExocoreNetwork/exocore/x/dogfood/types"; // GenesisState defines the dogfood module's genesis state. message GenesisState { + // Parameters of the module. Params params = 1 [(gogoproto.nullable) = false]; + + // Validator set, stored by ExocoreValidatorKey. + repeated .tendermint.abci.ValidatorUpdate val_set = 2 + [ (gogoproto.nullable) = false ]; } diff --git a/proto/exocore/dogfood/v1/params.proto b/proto/exocore/dogfood/v1/params.proto index 3305239a6..4d3f94a8d 100644 --- a/proto/exocore/dogfood/v1/params.proto +++ b/proto/exocore/dogfood/v1/params.proto @@ -9,5 +9,15 @@ option go_package = "github.com/ExocoreNetwork/exocore/x/dogfood/types"; // Params defines the parameters for the module. message Params { option (gogoproto.goproto_stringer) = false; - + // EpochsUntilUnbonded is the number of epochs after which an unbonding + // is released. Note that it starts from the beginning of the next epoch + // in which the unbonding request was received. At that point, the vote + // power is reduced by the amount of the unbonding operation. + uint32 epochs_until_unbonded = 1; + // EpochIdentifier is the identifier of the epoch (week, hour, day). + string epoch_identifier = 2; + // MaxValidators is the maximum number of validators. + uint32 max_validators = 3; + // HistoricalEntries is the number of historical entries to persist. + uint32 historical_entries = 4; } diff --git a/x/dogfood/genesis.go b/x/dogfood/genesis.go deleted file mode 100644 index 0c4077eb6..000000000 --- a/x/dogfood/genesis.go +++ /dev/null @@ -1,20 +0,0 @@ -package dogfood - -import ( - "github.com/ExocoreNetwork/exocore/x/dogfood/keeper" - "github.com/ExocoreNetwork/exocore/x/dogfood/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// InitGenesis initializes the module's state from a provided genesis state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { - k.SetParams(ctx, genState.Params) -} - -// ExportGenesis returns the module's exported genesis -func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { - genesis := types.DefaultGenesis() - genesis.Params = k.GetParams(ctx) - - return genesis -} diff --git a/x/dogfood/keeper/abci.go b/x/dogfood/keeper/abci.go new file mode 100644 index 000000000..30dd3950b --- /dev/null +++ b/x/dogfood/keeper/abci.go @@ -0,0 +1,67 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) EndBlock(ctx sdk.Context) []abci.ValidatorUpdate { + // start with clearing the hold on the undelegations. + undelegations := k.GetPendingUndelegations(ctx) + for _, undelegation := range undelegations.GetList() { + k.delegationKeeper.DecrementUndelegationHoldCount(ctx, undelegation) + } + k.ClearPendingUndelegations(ctx) + // then, let the operator module know that the opt out has finished. + optOuts := k.GetPendingOptOuts(ctx) + for _, addr := range optOuts.GetList() { + k.operatorKeeper.CompleteOperatorOptOutFromChainId(ctx, addr, ctx.ChainID()) + } + k.ClearPendingOptOuts(ctx) + // for slashing, the operator module is required to store a mapping of chain id + cons addr + // to operator address. this information can now be pruned, since the opt out is considered + // complete. + consensusAddrs := k.GetPendingConsensusAddrs(ctx) + for _, consensusAddr := range consensusAddrs.GetList() { + k.operatorKeeper.DeleteOperatorAddressForChainIdAndConsAddr( + ctx, ctx.ChainID(), consensusAddr, + ) + } + k.ClearPendingConsensusAddrs(ctx) + // finally, perform the actual operations of vote power changes. + operations := k.GetPendingOperations(ctx) + id, _ := k.GetValidatorSetID(ctx, ctx.BlockHeight()) + if len(operations.GetList()) == 0 { + // there is no validator set change, so we just increment the block height + // and retain the same val set id mapping. + k.SetValidatorSetID(ctx, ctx.BlockHeight()+1, id) + return []abci.ValidatorUpdate{} + } + res := make([]abci.ValidatorUpdate, 0, len(operations.GetList())) + for _, operation := range operations.GetList() { + switch operation.OperationType { + case types.KeyAdditionOrUpdate: + power, err := k.restakingKeeper.GetOperatorAssetValue( + ctx, operation.OperatorAddress, + ) + if err != nil { + panic(err) + } + res = append(res, abci.ValidatorUpdate{ + PubKey: operation.PubKey, + Power: power, + }) + case types.KeyRemoval: + res = append(res, abci.ValidatorUpdate{ + PubKey: operation.PubKey, + Power: 0, + }) + case types.KeyOpUnspecified: + panic("unspecified operation type") + } + } + // call via wrapper function so that validator info is stored. + // the id is incremented by 1 for the next block. + return k.ApplyValidatorChanges(ctx, res, id+1, false) +} diff --git a/x/dogfood/keeper/genesis.go b/x/dogfood/keeper/genesis.go new file mode 100644 index 000000000..e0544924d --- /dev/null +++ b/x/dogfood/keeper/genesis.go @@ -0,0 +1,33 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func (k Keeper) InitGenesis( + ctx sdk.Context, + genState types.GenesisState, +) []abci.ValidatorUpdate { + k.SetParams(ctx, genState.Params) + // the `params` validator is not super useful to validate state level information + // so, it must be done here. by extension, the `InitGenesis` of the epochs module + // should be called before that of this module. + _, found := k.epochsKeeper.GetEpochInfo(ctx, genState.Params.EpochIdentifier) + if !found { + // the panic is suitable here because it is being done at genesis, when the node + // is not running. it means that the genesis file is malformed. + panic("epoch info not found") + } + return k.ApplyValidatorChanges(ctx, genState.ValSet, types.InitialValidatorSetID, true) +} + +// ExportGenesis returns the module's exported genesis +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Params = k.GetDogfoodParams(ctx) + + return genesis +} diff --git a/x/dogfood/keeper/hooks.go b/x/dogfood/keeper/hooks.go new file mode 100644 index 000000000..cfbd66959 --- /dev/null +++ b/x/dogfood/keeper/hooks.go @@ -0,0 +1,34 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// interface guard +var _ types.DogfoodHooks = &MultiDogfoodHooks{} + +// MultiDogfoodHooks is a collection of DogfoodHooks. It calls the hook for each element in the +// collection one-by-one. The hook is called in the order in which the collection is created. +type MultiDogfoodHooks []types.DogfoodHooks + +// NewMultiDogfoodHooks is used to create a collective object of dogfood hooks from a list of +// the hooks. It follows the "accept interface, return concrete types" philosophy. Other modules +// may set the hooks by calling k := (*k).SetHooks(NewMultiDogfoodHooks(hookI)) +func NewMultiDogfoodHooks(hooks ...types.DogfoodHooks) MultiDogfoodHooks { + return hooks +} + +// AfterValidatorBonded is the implementation of types.DogfoodHooks for MultiDogfoodHooks. +func (hooks MultiDogfoodHooks) AfterValidatorBonded( + ctx sdk.Context, + consAddr sdk.ConsAddress, + operator sdk.ValAddress, +) error { + for _, hook := range hooks { + if err := hook.AfterValidatorBonded(ctx, consAddr, operator); err != nil { + return err + } + } + return nil +} diff --git a/x/dogfood/keeper/impl_delegation_hooks.go b/x/dogfood/keeper/impl_delegation_hooks.go new file mode 100644 index 000000000..e32442853 --- /dev/null +++ b/x/dogfood/keeper/impl_delegation_hooks.go @@ -0,0 +1,126 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DelegationHooksWrapper is the wrapper structure that implements the delegation hooks for the +// dogfood keeper. +type DelegationHooksWrapper struct { + keeper *Keeper +} + +// Interface guard +var _ types.DelegationHooks = DelegationHooksWrapper{} + +// DelegationHooks returns the delegation hooks wrapper. It follows the "accept interfaces, +// return concretes" pattern. +func (k *Keeper) DelegationHooks() DelegationHooksWrapper { + return DelegationHooksWrapper{k} +} + +// AfterDelegation is called after a delegation is made. +func (wrapper DelegationHooksWrapper) AfterDelegation( + ctx sdk.Context, operator sdk.AccAddress, +) { + found, pubKey, err := wrapper.keeper.operatorKeeper.GetOperatorConsKeyForChainId( + ctx, operator, ctx.ChainID(), + ) + if err != nil { + // the operator keeper can offer two errors: not an operator and not a chain. + // both of these should not happen here because the dogfooding genesis will + // register the chain, and the operator must be known to the delegation module + // when it calls this hook. + panic(err) + } + if found { + if !wrapper.keeper.operatorKeeper.IsOperatorOptingOutFromChainId( + ctx, operator, ctx.ChainID(), + ) { + // only queue the operation if operator is still opted into the chain. + res := wrapper.keeper.QueueOperation( + ctx, operator, pubKey, types.KeyAdditionOrUpdate, + ) + switch res { + case types.QueueResultExists: + // nothing to do because the operation is in the queue already. + case types.QueueResultRemoved: + // a KeyRemoval was in the queue which has now been cleared from the queue. + // the KeyRemoval can only be in the queue if the operator is opting out from + // the chain, or has replaced their key. if it is the former, it means that + // there is some inconsistency. if it is the latter, it means that the operator + // module just reported the old key in `GetOperatorConsKeyForChainId`, which + // should not happen. + panic("unexpected removal of operation from queue") + case types.QueueResultSuccess: + // best case, nothing to do. + case types.QueueResultUnspecified: + panic("unspecified queue result") + } + } + } +} + +// AfterUndelegationStarted is called after an undelegation is started. +func (wrapper DelegationHooksWrapper) AfterUndelegationStarted( + ctx sdk.Context, operator sdk.AccAddress, recordKey []byte, +) { + found, pubKey, err := wrapper.keeper.operatorKeeper.GetOperatorConsKeyForChainId( + ctx, operator, ctx.ChainID(), + ) + if err != nil { + panic(err) + } + if found { + // note that this is still key addition or update because undelegation does not remove + // the operator from the list. it only decreases their vote power. + if !wrapper.keeper.operatorKeeper.IsOperatorOptingOutFromChainId( + ctx, operator, ctx.ChainID(), + ) { + // only queue the operation if operator is still opted into the chain. + res := wrapper.keeper.QueueOperation( + ctx, operator, pubKey, types.KeyAdditionOrUpdate, + ) + switch res { + case types.QueueResultExists: + // nothing to do + case types.QueueResultRemoved: + // KeyRemoval + KeyAdditionOrUpdate => Removed + // KeyRemoval can happen + // 1. if the operator is opting out from the chain,which is inconsistent. + // 2. if the operator is replacing their old key, which should not be returned + // by `GetOperatorConsKeyForChainId`. + panic("unexpected removal of operation from queue") + case types.QueueResultSuccess: + // best case, nothing to do. + case types.QueueResultUnspecified: + panic("unspecified queue result") + } + } + // now handle the unbonding timeline. + wrapper.keeper.delegationKeeper.IncrementUndelegationHoldCount(ctx, recordKey) + // mark for unbonding release. + // note that we aren't supporting redelegation yet, so this undelegated amount will be + // held until the end of the unbonding period or the operator opt out period, whichever + // is first. + var unbondingCompletionEpoch int64 + if wrapper.keeper.operatorKeeper.IsOperatorOptingOutFromChainId( + ctx, operator, ctx.ChainID(), + ) { + unbondingCompletionEpoch = wrapper.keeper.GetOperatorOptOutFinishEpoch( + ctx, operator, + ) + } else { + unbondingCompletionEpoch = wrapper.keeper.GetUnbondingCompletionEpoch(ctx) + } + wrapper.keeper.AppendUndelegationToMature(ctx, unbondingCompletionEpoch, recordKey) + } +} + +// AfterUndelegationCompleted is called after an undelegation is completed. +func (DelegationHooksWrapper) AfterUndelegationCompleted( + sdk.Context, sdk.AccAddress, []byte, +) { + // no-op +} diff --git a/x/dogfood/keeper/impl_epochs_hooks.go b/x/dogfood/keeper/impl_epochs_hooks.go new file mode 100644 index 000000000..af7cc5267 --- /dev/null +++ b/x/dogfood/keeper/impl_epochs_hooks.go @@ -0,0 +1,60 @@ +package keeper + +import ( + "strings" + + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// EpochsHooksWrapper is the wrapper structure that implements the epochs hooks for the dogfood +// keeper. +type EpochsHooksWrapper struct { + keeper *Keeper +} + +// Interface guard +var _ types.EpochsHooks = EpochsHooksWrapper{} + +// EpochsHooks returns the epochs hooks wrapper. It follows the "accept interfaces, return +// concretes" pattern. +func (k *Keeper) EpochsHooks() EpochsHooksWrapper { + return EpochsHooksWrapper{k} +} + +// AfterEpochEnd is called after an epoch ends. +func (wrapper EpochsHooksWrapper) AfterEpochEnd( + ctx sdk.Context, identifier string, epoch int64, +) { + if strings.Compare(identifier, wrapper.keeper.GetEpochIdentifier(ctx)) == 0 { + // we will upgrade all of the queued information to "pending", which will be applied at + // the end of the block. + // note that this hook is called during BeginBlock, and the "pending" operations will be + // applied within this block. however, for clarity, it is highlighted that unbonding + // takes N epochs + 1 block to complete. + operations := wrapper.keeper.GetQueuedOperations(ctx) + wrapper.keeper.SetPendingOperations(ctx, types.Operations{List: operations}) + wrapper.keeper.ClearQueuedOperations(ctx) + optOuts := wrapper.keeper.GetOptOutsToFinish(ctx, epoch) + wrapper.keeper.SetPendingOptOuts(ctx, types.AccountAddresses{List: optOuts}) + wrapper.keeper.ClearOptOutsToFinish(ctx, epoch) + consAddresses := wrapper.keeper.GetConsensusAddrsToPrune(ctx, epoch) + wrapper.keeper.SetPendingConsensusAddrs( + ctx, types.ConsensusAddresses{List: consAddresses}, + ) + wrapper.keeper.ClearConsensusAddrsToPrune(ctx, epoch) + undelegations := wrapper.keeper.GetUndelegationsToMature(ctx, epoch) + wrapper.keeper.SetPendingUndelegations( + ctx, types.UndelegationRecordKeys{ + List: undelegations, + }) + wrapper.keeper.ClearUndelegationsToMature(ctx, epoch) + } +} + +// BeforeEpochStart is called before an epoch starts. +func (wrapper EpochsHooksWrapper) BeforeEpochStart( + ctx sdk.Context, identifier string, epoch int64, +) { + // nothing to do +} diff --git a/x/dogfood/keeper/impl_operator_hooks.go b/x/dogfood/keeper/impl_operator_hooks.go new file mode 100644 index 000000000..3599d6565 --- /dev/null +++ b/x/dogfood/keeper/impl_operator_hooks.go @@ -0,0 +1,147 @@ +package keeper + +import ( + "strings" + + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// OperatorHooksWrapper is the wrapper structure that implements the operator hooks for the +// dogfood keeper. +type OperatorHooksWrapper struct { + keeper *Keeper +} + +// Interface guards +var _ types.OperatorHooks = OperatorHooksWrapper{} + +func (k *Keeper) OperatorHooks() OperatorHooksWrapper { + return OperatorHooksWrapper{k} +} + +// Hooks assumptions: Assuming I is opt-in, O is opt-out and R is key replacement, these are all +// possible within the same epoch, for a fresh operator. +// I O +// I R +// I R O +// This is not possible for a fresh operator to do: +// I O R +// R I O +// R I +// For an operator that is already opted in, the list looks like follows: +// R O +// O I (reversing the decision to opt out) +// O I R +// R O I +// The impossible list looks like: +// O R +// O R I +// Replacing the key with the same key is not possible, so it is not covered. +// The assumption is that these are enforced (allowed/denied) by the operator module. + +// Cases for a fresh operator: +// I + O => KeyAdditionOrUpdate + KeyRemoval => Success + Removed => covered. +// I + R => KeyAdditionOrUpdate (old) + KeyAdditionOrUpdate (new) + KeyRemoval (old) => +// Success + Success + Removed + => not considered unbonding of the old key since it +// was not yet effective => covered. +// I + R + O => KeyAdditionOrUpdate (old) + KeyAdditionOrUpdate (new) + KeyRemoval (old) + +// KeyRemoval (new) => +// Success (old) + Success (new) + Removed (old) + Removed (new) => covered + +// Cases for an operator that has already opted in: +// R + O => KeyAdditionOrUpdate (new) + KeyRemoval (old) + KeyRemoval (new) => +// Success (new) + Success (old) + Removed (new) => +// unbonding data made (old) => covered. +// O + I +// O + I case 1 => KeyRemoval (old) + KeyAdditionOrUpdate (new) => Success (old) + Success (new) +// => unbonding data made (old) => covered. +// O + I case 2 => KeyRemoval (old) + KeyAdditionOrUpdate (old) => Success (old) + Removed (old) +// => unbonding data made (old) and then cleared => covered. +// O + I + R +// O + I + R case 1 => KeyRemoval (old) + KeyAdditionOrUpdate (old) + KeyRemoval (old) + +// KeyAdditionOrUpdate (new) => Success (old) + Removed (old) + Success (old) + +// Success (new) => unbonding data old made + cleared + made => covered. +// O + I + R case 2 => +// AfterOperatorOptOut(old) => Success => unbonding data made for old +// AfterOperatorOptIn(new) => Success => no data changed +// AfterOperatorKeyReplacement(new, new2) => +// new2 operation KeyAdditionOrUpdate => Success => no data changed +// new operation KeyRemoval => Removed => no data changed +// => covered +// R + O + I => KeyAdditionOrUpdate (new) + KeyRemoval (old) + KeyRemoval (new) + +// KeyAdditionOrUpdate (X) +// Success + Success (=> unbonding data for old) + Removed (=> no data) + +// case 1 => X == new => Success => no change => covered +// case 2 => X == old => Removed => unbonding data for old removed => covered. +// case 3 => X == abc => Success => no change and unbonding data for old is not removed => +// covered. + +// AfterOperatorOptIn is the implementation of the operator hooks. +func (h OperatorHooksWrapper) AfterOperatorOptIn( + ctx sdk.Context, addr sdk.AccAddress, + chainID string, pubKey tmprotocrypto.PublicKey, +) { + if strings.Compare(ctx.ChainID(), chainID) == 0 { + // res == Removed, it means operator has opted back in + // res == Success, there is no additional information to store + // res == Exists, there is nothing to do + if res := h.keeper.QueueOperation( + ctx, addr, pubKey, types.KeyAdditionOrUpdate, + ); res == types.QueueResultRemoved { + // the old operation was key removal, which is now removed from the queue. + // so all of the changes that were associated with it need to be undone. + h.keeper.ClearUnbondingInformation(ctx, addr, pubKey) + } + } +} + +// AfterOperatorKeyReplacement is the implementation of the operator hooks. +func (h OperatorHooksWrapper) AfterOperatorKeyReplacement( + ctx sdk.Context, addr sdk.AccAddress, + newKey tmprotocrypto.PublicKey, oldKey tmprotocrypto.PublicKey, + chainID string, +) { + if strings.Compare(chainID, ctx.ChainID()) == 0 { + // remove the old key + // res == Removed, it means operator had added this key and is now removing it. + // no additional information to clear. + // res == Success, the old key should be pruned from the operator module. + // res == Exists, there is nothing to do. + if res := h.keeper.QueueOperation( + ctx, addr, oldKey, types.KeyRemoval, + ); res == types.QueueResultSuccess { + // the old key can be marked for pruning + h.keeper.SetUnbondingInformation(ctx, addr, oldKey, false) + } + // add the new key + // res == Removed, it means operator has added their original key again + // res == Success, there is no additional information to store + // res == Exists, there is no nothing to do + if res := h.keeper.QueueOperation( + ctx, addr, newKey, types.KeyAdditionOrUpdate, + ); res == types.QueueResultRemoved { + // see AfterOperatorOptIn for explanation + h.keeper.ClearUnbondingInformation(ctx, addr, newKey) + } + } +} + +// AfterOperatorOptOutInitiated is the implementation of the operator hooks. +func (h OperatorHooksWrapper) AfterOperatorOptOutInitiated( + ctx sdk.Context, addr sdk.AccAddress, + chainID string, pubKey tmprotocrypto.PublicKey, +) { + if strings.Compare(chainID, ctx.ChainID()) == 0 { + // res == Removed means operator had opted in and is now opting out. nothing to do if + // it is within the same epoch. + // res == Success, set up pruning deadline and opt out completion deadline + // res == Exists, there is nothing to do (should never happen) + if res := h.keeper.QueueOperation( + ctx, addr, pubKey, types.KeyRemoval, + ); res == types.QueueResultSuccess { + h.keeper.SetUnbondingInformation(ctx, addr, pubKey, true) + } + } +} diff --git a/x/dogfood/keeper/impl_sdk.go b/x/dogfood/keeper/impl_sdk.go new file mode 100644 index 000000000..547f526e7 --- /dev/null +++ b/x/dogfood/keeper/impl_sdk.go @@ -0,0 +1,171 @@ +package keeper + +import ( + "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" +) + +// interface guards +var _ slashingtypes.StakingKeeper = Keeper{} +var _ evidencetypes.StakingKeeper = Keeper{} +var _ genutiltypes.StakingKeeper = Keeper{} +var _ clienttypes.StakingKeeper = Keeper{} // implemented in `validators.go` + +// GetParams is an implementation of the staking interface expected by the SDK's evidence +// module. The module does not use it, but it is part of the interface. +func (k Keeper) GetParams(ctx sdk.Context) stakingtypes.Params { + return stakingtypes.Params{} +} + +// IterateValidators is an implementation of the staking interface expected by the SDK's +// slashing module. The slashing module uses it for two purposes: once at genesis to +// store a mapping of pub key to cons address (which is done by our operator module), +// and then during the invariants check to ensure that the total delegated amount +// matches that of each validator. Ideally, this invariant should be implemented +// by the delegation and/or deposit module(s) instead. +func (k Keeper) IterateValidators(sdk.Context, + func(index int64, validator stakingtypes.ValidatorI) (stop bool)) { + // no op +} + +// Validator is an implementation of the staking interface expected by the SDK's +// slashing module. The slashing module uses it to obtain a validator's information at +// its addition to the list of validators, and then to unjail a validator. The former +// is used to create the pub key to cons address mapping, which we do in the operator module. +// The latter should also be implemented in the operator module, or maybe the slashing module +// depending upon the finalized design. We don't need to implement this function here because +// we are not calling the AfterValidatorCreated hook in our module, so this will never be +// reached. +func (k Keeper) Validator(ctx sdk.Context, addr sdk.ValAddress) stakingtypes.ValidatorI { + panic("unimplemented on this keeper") +} + +// ValidatorByConsAddr is an implementation of the staking interface expected by the SDK's +// slashing and evidence modules. +// The slashing module calls this function when it observes downtime. The only requirement on +// the returned value is that it isn't nil, and the jailed status is accurately set (to prevent +// re-jailing of the same operator). +// The evidence module calls this function when it handles equivocation evidence. The returned +// value must not be nil and must not have an UNBONDED validator status (the default is +// unspecified), or evidence will reject it. +func (k Keeper) ValidatorByConsAddr( + ctx sdk.Context, + addr sdk.ConsAddress, +) stakingtypes.ValidatorI { + found, accAddr := k.operatorKeeper.GetOperatorAddressForChainIdAndConsAddr( + ctx, ctx.ChainID(), addr, + ) + if !found { + // replicate the behavior of the SDK's staking module + return nil + } + return stakingtypes.Validator{ + Jailed: k.operatorKeeper.IsOperatorJailedForChainId(ctx, accAddr, ctx.ChainID()), + } +} + +// Slash is an implementation of the staking interface expected by the SDK's slashing module. +// It forwards the call to SlashWithInfractionReason with Infraction_INFRACTION_UNSPECIFIED. +// It is not called within the slashing module, but is part of the interface. +func (k Keeper) Slash( + ctx sdk.Context, addr sdk.ConsAddress, + infractionHeight, power int64, + slashFactor sdk.Dec, +) math.Int { + return k.SlashWithInfractionReason( + ctx, addr, infractionHeight, power, + slashFactor, stakingtypes.Infraction_INFRACTION_UNSPECIFIED, + ) +} + +// SlashWithInfractionReason is an implementation of the staking interface expected by the +// SDK's slashing module. It is called when the slashing module observes an infraction +// of either downtime or equivocation (which is via the evidence module). +func (k Keeper) SlashWithInfractionReason( + ctx sdk.Context, addr sdk.ConsAddress, infractionHeight, power int64, + slashFactor sdk.Dec, infraction stakingtypes.Infraction, +) math.Int { + found, accAddress := k.operatorKeeper.GetOperatorAddressForChainIdAndConsAddr( + ctx, ctx.ChainID(), addr, + ) + if !found { + // TODO(mm): already slashed and removed from the set? + return math.NewInt(0) + } + // TODO(mm): add list of assets to be slashed (and not just all of them). + // based on yet to be finalized slashing design. + return k.slashingKeeper.SlashWithInfractionReason( + ctx, accAddress, infractionHeight, + power, slashFactor, infraction, + ) +} + +// Jail is an implementation of the staking interface expected by the SDK's slashing module. +// It delegates the call to the operator module. Alternatively, this may be handled +// by the slashing module depending upon the design decisions. +func (k Keeper) Jail(ctx sdk.Context, addr sdk.ConsAddress) { + k.operatorKeeper.Jail(ctx, addr, ctx.ChainID()) + // TODO(mm) + // once the operator module jails someone, a hook should be triggered + // and the validator removed from the set. same for unjailing. +} + +// Unjail is an implementation of the staking interface expected by the SDK's slashing module. +// The function is called by the slashing module only when it receives a request from the +// operator to do so. TODO(mm): We need to use the SDK's slashing module to allow for downtime +// slashing but somehow we need to prevent its Unjail function from being called by anyone. +func (k Keeper) Unjail(sdk.Context, sdk.ConsAddress) { + panic("unimplemented on this keeper") +} + +// Delegation is an implementation of the staking interface expected by the SDK's slashing +// module. The slashing module uses it to obtain the delegation information of a validator +// before unjailing it. If the slashing module's unjail function is never called, this +// function will never be called either. +func (k Keeper) Delegation( + sdk.Context, sdk.AccAddress, sdk.ValAddress, +) stakingtypes.DelegationI { + panic("unimplemented on this keeper") +} + +// MaxValidators is an implementation of the staking interface expected by the SDK's slashing +// module. It is not called within the slashing module, but is part of the interface. +// It returns the maximum number of validators allowed in the network. +func (k Keeper) MaxValidators(ctx sdk.Context) uint32 { + return k.GetMaxValidators(ctx) +} + +// GetAllValidators is an implementation of the staking interface expected by the SDK's +// slashing module. It is not called within the slashing module, but is part of the interface. +func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []stakingtypes.Validator) { + return []stakingtypes.Validator{} +} + +// IsValidatorJailed is an implementation of the staking interface expected by the SDK's +// slashing module. It is called by the slashing module to record validator signatures +// for downtime tracking. We delegate the call to the operator keeper. +func (k Keeper) IsValidatorJailed(ctx sdk.Context, addr sdk.ConsAddress) bool { + found, accAddr := k.operatorKeeper.GetOperatorAddressForChainIdAndConsAddr( + ctx, ctx.ChainID(), addr, + ) + if !found { + // replicate the behavior of the SDK's staking module + return false + } + return k.operatorKeeper.IsOperatorJailedForChainId(ctx, accAddr, ctx.ChainID()) +} + +// ApplyAndReturnValidatorSetUpdates is an implementation of the staking interface expected +// by the SDK's genutil module. It is used in the gentx command, which we do not need to +// support. So this function does nothing. +func (k Keeper) ApplyAndReturnValidatorSetUpdates( + sdk.Context, +) (updates []abci.ValidatorUpdate, err error) { + return +} diff --git a/x/dogfood/keeper/keeper.go b/x/dogfood/keeper/keeper.go index 5abed0dd1..e85f961d0 100644 --- a/x/dogfood/keeper/keeper.go +++ b/x/dogfood/keeper/keeper.go @@ -17,13 +17,29 @@ type ( cdc codec.BinaryCodec storeKey storetypes.StoreKey paramstore paramtypes.Subspace + + // internal hooks to allow other modules to subscriber to our events + dogfoodHooks types.DogfoodHooks + + // external keepers as interfaces + epochsKeeper types.EpochsKeeper + operatorKeeper types.OperatorKeeper + delegationKeeper types.DelegationKeeper + restakingKeeper types.RestakingKeeper + slashingKeeper types.SlashingKeeper } ) +// NewKeeper creates a new dogfood keeper. func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, ps paramtypes.Subspace, + epochsKeeper types.EpochsKeeper, + operatorKeeper types.OperatorKeeper, + delegationKeeper types.DelegationKeeper, + restakingKeeper types.RestakingKeeper, + slashingKeeper types.SlashingKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -31,12 +47,71 @@ func NewKeeper( } return &Keeper{ - cdc: cdc, - storeKey: storeKey, - paramstore: ps, + cdc: cdc, + storeKey: storeKey, + paramstore: ps, + epochsKeeper: epochsKeeper, + operatorKeeper: operatorKeeper, + delegationKeeper: delegationKeeper, + restakingKeeper: restakingKeeper, + slashingKeeper: slashingKeeper, } } +// Logger returns a logger object for use within the module. func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } + +// SetHooks sets the hooks on the keeper. It intentionally has a pointer receiver so that +// changes can be saved to the object. +func (k *Keeper) SetHooks(sh types.DogfoodHooks) *Keeper { + if k.dogfoodHooks != nil { + panic("cannot set dogfood hooks twice") + } + if sh == nil { + panic("cannot set nil dogfood hooks") + } + k.dogfoodHooks = sh + return k +} + +// Hooks returns the hooks registered to the module. +func (k Keeper) Hooks() types.DogfoodHooks { + return k.dogfoodHooks +} + +// GetQueuedKeyOperations returns the list of operations that are queued for execution at the +// end of the current epoch. +func (k Keeper) GetQueuedOperations( + ctx sdk.Context, +) []types.Operation { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.QueuedOperationsKey()) + if bz == nil { + return []types.Operation{} + } + var operations types.Operations + if err := operations.Unmarshal(bz); err != nil { + // TODO(mm): any failure to unmarshal is treated as no operations or panic? + return []types.Operation{} + } + return operations.GetList() +} + +// ClearQueuedOperations clears the operations to be executed at the end of the epoch. +func (k Keeper) ClearQueuedOperations(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.QueuedOperationsKey()) +} + +// setQueuedOperations is a private, internal function used to update the current queue of +// operations to be executed at the end of the epoch with the supplied value. +func (k Keeper) setQueuedOperations(ctx sdk.Context, operations types.Operations) { + store := ctx.KVStore(k.storeKey) + bz, err := operations.Marshal() + if err != nil { + panic(err) + } + store.Set(types.QueuedOperationsKey(), bz) +} diff --git a/x/dogfood/keeper/opt_out.go b/x/dogfood/keeper/opt_out.go new file mode 100644 index 000000000..939d747e3 --- /dev/null +++ b/x/dogfood/keeper/opt_out.go @@ -0,0 +1,162 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// AppendOptOutToFinish appends an operator address to the list of operator addresses that have +// opted out and will be finished at the end of the provided epoch. +func (k Keeper) AppendOptOutToFinish( + ctx sdk.Context, epoch int64, operatorAddr sdk.AccAddress, +) { + prev := k.GetOptOutsToFinish(ctx, epoch) + next := types.AccountAddresses{List: append(prev, operatorAddr)} + k.setOptOutsToFinish(ctx, epoch, next) +} + +// GetOptOutsToFinish returns the list of operator addresses that have opted out and will be +// finished at the end of the provided epoch. +func (k Keeper) GetOptOutsToFinish( + ctx sdk.Context, epoch int64, +) [][]byte { + store := ctx.KVStore(k.storeKey) + key := types.OptOutsToFinishKey(epoch) + bz := store.Get(key) + if bz == nil { + return [][]byte{} + } + var res types.AccountAddresses + if err := res.Unmarshal(bz); err != nil { + panic(err) + } + return res.GetList() +} + +// setOptOutsToFinish sets the list of operator addresses that have opted out and will be +// finished at the end of the provided epoch. +func (k Keeper) setOptOutsToFinish( + ctx sdk.Context, epoch int64, addrs types.AccountAddresses, +) { + store := ctx.KVStore(k.storeKey) + key := types.OptOutsToFinishKey(epoch) + bz, err := addrs.Marshal() + if err != nil { + panic(err) + } + store.Set(key, bz) +} + +// RemoveOptOutToFinish removes an operator address from the list of operator addresses that +// have opted out and will be finished at the end of the provided epoch. +func (k Keeper) RemoveOptOutToFinish(ctx sdk.Context, epoch int64, addr sdk.AccAddress) { + prev := k.GetOptOutsToFinish(ctx, epoch) + next := types.AccountAddresses{ + List: types.RemoveFromBytesList(prev, addr), + } + k.setOptOutsToFinish(ctx, epoch, next) +} + +// ClearOptOutsToFinish clears the list of operator addresses that have opted out and will be +// finished at the end of the provided epoch. +func (k Keeper) ClearOptOutsToFinish(ctx sdk.Context, epoch int64) { + store := ctx.KVStore(k.storeKey) + key := types.OptOutsToFinishKey(epoch) + store.Delete(key) +} + +// SetOperatorOptOutFinishEpoch sets the epoch at which an operator's opt out will be finished. +func (k Keeper) SetOperatorOptOutFinishEpoch( + ctx sdk.Context, operatorAddr sdk.AccAddress, epoch int64, +) { + store := ctx.KVStore(k.storeKey) + key := types.OperatorOptOutFinishEpochKey(operatorAddr) + bz := sdk.Uint64ToBigEndian(uint64(epoch)) + store.Set(key, bz) +} + +// GetOperatorOptOutFinishEpoch returns the epoch at which an operator's opt out will be +// finished. +func (k Keeper) GetOperatorOptOutFinishEpoch( + ctx sdk.Context, operatorAddr sdk.AccAddress, +) int64 { + store := ctx.KVStore(k.storeKey) + key := types.OperatorOptOutFinishEpochKey(operatorAddr) + bz := store.Get(key) + if bz == nil { + return -1 + } + return int64(sdk.BigEndianToUint64(bz)) +} + +// DeleteOperatorOptOutFinishEpoch deletes the epoch at which an operator's opt out will be +// finished. +func (k Keeper) DeleteOperatorOptOutFinishEpoch( + ctx sdk.Context, operatorAddr sdk.AccAddress, +) { + store := ctx.KVStore(k.storeKey) + key := types.OperatorOptOutFinishEpochKey(operatorAddr) + store.Delete(key) +} + +// AppendConsensusAddrToPrune appends a consensus address to the list of consensus addresses to +// prune at the end of the epoch. +func (k Keeper) AppendConsensusAddrToPrune( + ctx sdk.Context, epoch int64, operatorAddr sdk.ConsAddress, +) { + prev := k.GetConsensusAddrsToPrune(ctx, epoch) + next := types.ConsensusAddresses{List: append(prev, operatorAddr)} + k.setConsensusAddrsToPrune(ctx, epoch, next) +} + +// GetConsensusAddrsToPrune returns the list of consensus addresses to prune at the end of the +// epoch. +func (k Keeper) GetConsensusAddrsToPrune( + ctx sdk.Context, epoch int64, +) [][]byte { + store := ctx.KVStore(k.storeKey) + key := types.ConsensusAddrsToPruneKey(epoch) + bz := store.Get(key) + if bz == nil { + return [][]byte{} + } + var res types.ConsensusAddresses + if err := res.Unmarshal(bz); err != nil { + panic(err) + } + return res.GetList() +} + +// DeleteConsensusAddrToPrune deletes a consensus address from the list of consensus addresses +// to prune at the end of the provided epoch. +func (k Keeper) DeleteConsensusAddrToPrune( + ctx sdk.Context, epoch int64, addr sdk.ConsAddress, +) { + prev := k.GetConsensusAddrsToPrune(ctx, epoch) + next := types.ConsensusAddresses{ + List: types.RemoveFromBytesList(prev, addr.Bytes()), + } + k.setConsensusAddrsToPrune(ctx, epoch, next) +} + +// ClearConsensusAddrsToPrune clears the list of consensus addresses to prune at the end of the +// epoch. +func (k Keeper) ClearConsensusAddrsToPrune(ctx sdk.Context, epoch int64) { + store := ctx.KVStore(k.storeKey) + key := types.ConsensusAddrsToPruneKey(epoch) + store.Delete(key) +} + +// setConsensusAddrsToPrune sets the list of consensus addresses to prune at the end of the +// epoch. +func (k Keeper) setConsensusAddrsToPrune( + ctx sdk.Context, epoch int64, addrs types.ConsensusAddresses, +) { + store := ctx.KVStore(k.storeKey) + key := types.ConsensusAddrsToPruneKey(epoch) + bz, err := addrs.Marshal() + if err != nil { + panic(err) + } + store.Set(key, bz) +} diff --git a/x/dogfood/keeper/params.go b/x/dogfood/keeper/params.go index 95553df50..f2bb7af60 100644 --- a/x/dogfood/keeper/params.go +++ b/x/dogfood/keeper/params.go @@ -5,12 +5,52 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// GetParams get all parameters as types.Params -func (k Keeper) GetParams(ctx sdk.Context) types.Params { - return types.NewParams() +// GetEpochsUntilUnbonded returns the number of epochs after which an unbonding that is made +// during the current epoch will be released. It is a parameter of the dogfood module. +func (k Keeper) GetEpochsUntilUnbonded(ctx sdk.Context) uint32 { + var epochsUntilUnbonded uint32 + k.paramstore.Get(ctx, types.KeyEpochsUntilUnbonded, &epochsUntilUnbonded) + return epochsUntilUnbonded } -// SetParams set the params +// GetEpochIdentifier returns the epoch identifier used to measure an epoch. It is a parameter +// of the dogfood module. +func (k Keeper) GetEpochIdentifier(ctx sdk.Context) string { + var epochIdentifier string + k.paramstore.Get(ctx, types.KeyEpochIdentifier, &epochIdentifier) + return epochIdentifier +} + +// GetMaxValidators returns the maximum number of validators that can be asked to validate for +// the chain. It is a parameter of the dogfood module. +func (k Keeper) GetMaxValidators(ctx sdk.Context) uint32 { + var maxValidators uint32 + k.paramstore.Get(ctx, types.KeyMaxValidators, &maxValidators) + return maxValidators +} + +// GetHistorialEntries is the number of historical info entries to persist in the store. These +// entries are used by the IBC module. The return value is a parameter of the dogfood module. +func (k Keeper) GetHistoricalEntries(ctx sdk.Context) uint32 { + var historicalEntries uint32 + k.paramstore.Get(ctx, types.KeyHistoricalEntries, &historicalEntries) + return historicalEntries +} + +// SetParams sets the params for the dogfood module. func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramstore.SetParamSet(ctx, ¶ms) } + +// GetDogfoodParams returns the parameters for the dogfood module. Note that this function is +// intentionally called GetDogfoodParams and not GetParams, since the GetParams function is used +// to implement the slashingtypes.StakingKeeper interface `GetParams(sdk.Context) +// stakingtypes.Params`. +func (k Keeper) GetDogfoodParams(ctx sdk.Context) (params types.Params) { + return types.NewParams( + k.GetEpochsUntilUnbonded(ctx), + k.GetEpochIdentifier(ctx), + k.GetMaxValidators(ctx), + k.GetHistoricalEntries(ctx), + ) +} diff --git a/x/dogfood/keeper/pending.go b/x/dogfood/keeper/pending.go new file mode 100644 index 000000000..7d68779f8 --- /dev/null +++ b/x/dogfood/keeper/pending.go @@ -0,0 +1,132 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SetPendingOperations sets the pending operations to be applied at the end of the block. +func (k Keeper) SetPendingOperations(ctx sdk.Context, operations types.Operations) { + store := ctx.KVStore(k.storeKey) + bz, err := operations.Marshal() + if err != nil { + panic(err) + } + store.Set(types.PendingOperationsKey(), bz) +} + +// GetPendingOperations returns the pending operations to be applied at the end of the block. +func (k Keeper) GetPendingOperations(ctx sdk.Context) types.Operations { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PendingOperationsKey()) + if bz == nil { + return types.Operations{} + } + var operations types.Operations + if err := operations.Unmarshal(bz); err != nil { + panic(err) + } + return operations +} + +// ClearPendingOperations clears the pending operations to be applied at the end of the block. +func (k Keeper) ClearPendingOperations(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PendingOperationsKey()) +} + +// SetPendingOptOuts sets the pending opt-outs to be applied at the end of the block. +func (k Keeper) SetPendingOptOuts(ctx sdk.Context, addrs types.AccountAddresses) { + store := ctx.KVStore(k.storeKey) + bz, err := addrs.Marshal() + if err != nil { + panic(err) + } + store.Set(types.PendingOptOutsKey(), bz) +} + +// GetPendingOptOuts returns the pending opt-outs to be applied at the end of the block. +func (k Keeper) GetPendingOptOuts(ctx sdk.Context) types.AccountAddresses { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PendingOptOutsKey()) + if bz == nil { + return types.AccountAddresses{} + } + var addrs types.AccountAddresses + if err := addrs.Unmarshal(bz); err != nil { + panic(err) + } + return addrs +} + +// ClearPendingOptOuts clears the pending opt-outs to be applied at the end of the block. +func (k Keeper) ClearPendingOptOuts(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PendingOptOutsKey()) +} + +// SetPendingConsensusAddrs sets the pending consensus addresses to be pruned at the end of the +// block. +func (k Keeper) SetPendingConsensusAddrs(ctx sdk.Context, addrs types.ConsensusAddresses) { + store := ctx.KVStore(k.storeKey) + bz, err := addrs.Marshal() + if err != nil { + panic(err) + } + store.Set(types.PendingConsensusAddrsKey(), bz) +} + +// GetPendingConsensusAddrs returns the pending consensus addresses to be pruned at the end of +// the block. +func (k Keeper) GetPendingConsensusAddrs(ctx sdk.Context) types.ConsensusAddresses { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PendingConsensusAddrsKey()) + if bz == nil { + return types.ConsensusAddresses{} + } + var addrs types.ConsensusAddresses + if err := addrs.Unmarshal(bz); err != nil { + panic(err) + } + return addrs +} + +// ClearPendingConsensusAddrs clears the pending consensus addresses to be pruned at the end of +// the block. +func (k Keeper) ClearPendingConsensusAddrs(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PendingConsensusAddrsKey()) +} + +// SetPendingUndelegations sets the pending undelegations to be released at the end of the +// block. +func (k Keeper) SetPendingUndelegations(ctx sdk.Context, undelegations types.UndelegationRecordKeys) { + store := ctx.KVStore(k.storeKey) + bz, err := undelegations.Marshal() + if err != nil { + panic(err) + } + store.Set(types.PendingUndelegationsKey(), bz) +} + +// GetPendingUndelegations returns the pending undelegations to be released at the end of the +// block. +func (k Keeper) GetPendingUndelegations(ctx sdk.Context) types.UndelegationRecordKeys { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PendingUndelegationsKey()) + if bz == nil { + return types.UndelegationRecordKeys{} + } + var undelegations types.UndelegationRecordKeys + if err := undelegations.Unmarshal(bz); err != nil { + panic(err) + } + return undelegations +} + +// ClearPendingUndelegations clears the pending undelegations to be released at the end of the +// block. +func (k Keeper) ClearPendingUndelegations(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PendingUndelegationsKey()) +} diff --git a/x/dogfood/keeper/query.go b/x/dogfood/keeper/query.go index 34ed99320..60c010b19 100644 --- a/x/dogfood/keeper/query.go +++ b/x/dogfood/keeper/query.go @@ -21,5 +21,5 @@ func (k Keeper) Params( } ctx := sdk.UnwrapSDKContext(goCtx) - return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil + return &types.QueryParamsResponse{Params: k.GetDogfoodParams(ctx)}, nil } diff --git a/x/dogfood/keeper/queue.go b/x/dogfood/keeper/queue.go new file mode 100644 index 000000000..591962d5f --- /dev/null +++ b/x/dogfood/keeper/queue.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueueOperation adds an operation to the consensus public key queue. If a similar operation +// already exists, the queue is not modified and QueueResultExists is returned. If a reverse +// operation already exists (removal + addition, or addition + removal), the old operation is +// dropped from the queue and QueueResultRemoved is returned. In the case that the operation is +// added to the queue, QueueResultSuccess is returned. +func (k Keeper) QueueOperation( + ctx sdk.Context, addr sdk.AccAddress, + key tmprotocrypto.PublicKey, operationType types.OperationType, +) types.QueueResultType { + if operationType == types.KeyOpUnspecified { + // should never happen + panic("invalid operation type") + } + currentQueue := k.GetQueuedOperations(ctx) + indexToDelete := len(currentQueue) + for i, operation := range currentQueue { + if operation.PubKey.Equal(key) { + if operation.OperationType == operationType { + return types.QueueResultExists + } else { + indexToDelete = i + break + } + } + } + ret := types.QueueResultSuccess + if indexToDelete > len(currentQueue) { + currentQueue = append(currentQueue[:indexToDelete], currentQueue[indexToDelete+1:]...) + ret = types.QueueResultRemoved + } else { + operation := types.Operation{OperationType: operationType, OperatorAddress: addr, PubKey: key} + currentQueue = append(currentQueue, operation) + } + operations := types.Operations{List: currentQueue} + k.setQueuedOperations(ctx, operations) + return ret +} diff --git a/x/dogfood/keeper/unbonding.go b/x/dogfood/keeper/unbonding.go new file mode 100644 index 000000000..9e588e63a --- /dev/null +++ b/x/dogfood/keeper/unbonding.go @@ -0,0 +1,113 @@ +package keeper + +import ( + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ClearUnbondingInformation clears all information related to an operator's opt out +// or key replacement. This is done because the operator has opted back in or has +// replaced their key (again) with the original one. +func (k Keeper) ClearUnbondingInformation( + ctx sdk.Context, addr sdk.AccAddress, pubKey tmprotocrypto.PublicKey, +) { + optOutEpoch := k.GetOperatorOptOutFinishEpoch(ctx, addr) + k.DeleteOperatorOptOutFinishEpoch(ctx, addr) + k.RemoveOptOutToFinish(ctx, optOutEpoch, addr) + consAddress, err := types.TMCryptoPublicKeyToConsAddr(pubKey) + if err != nil { + panic(err) + } + k.DeleteConsensusAddrToPrune(ctx, optOutEpoch, consAddress) +} + +// SetUnbondingInformation sets information related to an operator's opt out or key replacement. +func (k Keeper) SetUnbondingInformation( + ctx sdk.Context, addr sdk.AccAddress, pubKey tmprotocrypto.PublicKey, isOptingOut bool, +) { + unbondingCompletionEpoch := k.GetUnbondingCompletionEpoch(ctx) + k.AppendOptOutToFinish(ctx, unbondingCompletionEpoch, addr) + if isOptingOut { + k.SetOperatorOptOutFinishEpoch(ctx, addr, unbondingCompletionEpoch) + } + consAddress, err := types.TMCryptoPublicKeyToConsAddr(pubKey) + if err != nil { + panic(err) + } + k.AppendConsensusAddrToPrune(ctx, unbondingCompletionEpoch, consAddress) +} + +// GetUnbondingCompletionEpoch returns the epoch at the end of which +// an unbonding triggered in this epoch will be completed. +func (k Keeper) GetUnbondingCompletionEpoch( + ctx sdk.Context, +) int64 { + unbondingEpochs := k.GetEpochsUntilUnbonded(ctx) + epochInfo, found := k.epochsKeeper.GetEpochInfo( + ctx, k.GetEpochIdentifier(ctx), + ) + if !found { + panic("current epoch not found") + } + // if i execute the transaction at epoch 5, the vote power change + // goes into effect at the beginning of epoch 6. the information + // should be held for 7 epochs, so it should be deleted at the + // beginning of epoch 13 or the end of epoch 12. + return epochInfo.CurrentEpoch + int64(unbondingEpochs) +} + +// AppendUndelegationsToMature stores that the undelegation with recordKey should be +// released at the end of the provided epoch. +func (k Keeper) AppendUndelegationToMature( + ctx sdk.Context, epoch int64, recordKey []byte, +) { + prev := k.GetUndelegationsToMature(ctx, epoch) + next := types.UndelegationRecordKeys{ + List: append(prev, recordKey), + } + k.setUndelegationsToMature(ctx, epoch, next) +} + +// GetUndelegationsToMature returns all undelegation entries that should be released +// at the end of the provided epoch. +func (k Keeper) GetUndelegationsToMature( + ctx sdk.Context, epoch int64, +) [][]byte { + store := ctx.KVStore(k.storeKey) + key := types.UnbondingReleaseMaturityKey(epoch) + bz := store.Get(key) + if bz == nil { + return [][]byte{} + } + var res types.UndelegationRecordKeys + if err := res.Unmarshal(bz); err != nil { + // should never happen + panic(err) + } + return res.GetList() +} + +// ClearUndelegationsToMature is a pruning method which is called after we mature +// the undelegation entries. +func (k Keeper) ClearUndelegationsToMature( + ctx sdk.Context, epoch int64, +) { + store := ctx.KVStore(k.storeKey) + key := types.UnbondingReleaseMaturityKey(epoch) + store.Delete(key) +} + +// setUndelegationsToMature sets all undelegation entries that should be released +// at the end of the provided epoch. +func (k Keeper) setUndelegationsToMature( + ctx sdk.Context, epoch int64, undelegationRecords types.UndelegationRecordKeys, +) { + store := ctx.KVStore(k.storeKey) + key := types.UnbondingReleaseMaturityKey(epoch) + val, err := undelegationRecords.Marshal() + if err != nil { + panic(err) + } + store.Set(key, val) +} diff --git a/x/dogfood/keeper/validators.go b/x/dogfood/keeper/validators.go new file mode 100644 index 000000000..c41b7ba22 --- /dev/null +++ b/x/dogfood/keeper/validators.go @@ -0,0 +1,356 @@ +// This file is a duplicate of the subscriber module's validators file with minor changes. +// The function ApplyValidatorChanges can likely be carved out into a shared package. + +package keeper + +import ( + "time" + + "github.com/ExocoreNetwork/exocore/x/dogfood/types" + abci "github.com/cometbft/cometbft/abci/types" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// UnbondingTime returns the time duration of the unbonding period. It is part of the +// implementation of the staking keeper expected by IBC. +// It is calculated as the number of epochs until unbonded multiplied by the duration of an +// epoch. This function is used by IBC's client keeper to validate the self client, and +// nowhere else. As long as it reports a consistent value, it's fine. +func (k Keeper) UnbondingTime(ctx sdk.Context) time.Duration { + count := k.GetEpochsUntilUnbonded(ctx) + identifier := k.GetEpochIdentifier(ctx) + // no need to check for found, as the epoch info is validated at genesis. + epoch, _ := k.epochsKeeper.GetEpochInfo(ctx, identifier) + durationPerEpoch := epoch.Duration + return time.Duration(count) * durationPerEpoch +} + +// ApplyValidatorChanges returns the validator set as is. However, it also +// stores the validators that are added or those that are removed, and updates +// the power for the existing validators. It also allows any hooks registered +// on the keeper to be executed. Lastly, it stores the validator set against the +// provided validator set id. +func (k Keeper) ApplyValidatorChanges( + ctx sdk.Context, changes []abci.ValidatorUpdate, valSetID uint64, genesis bool, +) []abci.ValidatorUpdate { + ret := []abci.ValidatorUpdate{} + for _, change := range changes { + // convert TM pubkey to SDK pubkey + pubkey, err := cryptocodec.FromTmProtoPublicKey(change.GetPubKey()) + if err != nil { + // An error here would indicate that the validator updates + // received from other modules (or genesis) are invalid. + panic(err) + } + addr := pubkey.Address() + val, found := k.GetValidator(ctx, addr) + + if found { + // update or delete an existing validator + if change.Power < 1 { + k.DeleteValidator(ctx, addr) + } else { + val.Power = change.Power + k.SetValidator(ctx, val) + } + } else if change.Power > 0 { + // create a new validator - the address is just derived from the public key and has + // no correlation with the operator address on Exocore + ocVal, err := types.NewExocoreValidator(addr, change.Power, pubkey) + if err != nil { + // An error here would indicate that the validator updates + // received are invalid. + panic(err) + } + + k.SetValidator(ctx, ocVal) + err = k.Hooks().AfterValidatorBonded(ctx, sdk.ConsAddress(addr), nil) + if err != nil { + // AfterValidatorBonded is hooked by the Slashing module and should not return + // an error. If any other module were to hook it, they should also not. + panic(err) + } + } else { + // edge case: we received an update for 0 power + // but the validator is already deleted. Do not forward + // to tendermint. + continue + } + ret = append(ret, change) + } + + // store the validator set against the provided validator set id + lastVals := types.Validators{} + for _, v := range k.GetAllExocoreValidators(ctx) { + pubkey, err := v.ConsPubKey() + if err != nil { + panic(err) + } + val, err := stakingtypes.NewValidator(nil, pubkey, stakingtypes.Description{}) + if err != nil { + panic(err) + } + // Set validator to bonded status + val.Status = stakingtypes.Bonded + // Compute tokens from voting power + val.Tokens = sdk.TokensFromConsensusPower(v.Power, sdk.DefaultPowerReduction) + lastVals.List = append(lastVals.GetList(), val) + } + k.SetValidatorSet(ctx, valSetID, &lastVals) + if !genesis { + // the val set change is effective as of the next block, so height + 1. + k.SetValidatorSetID(ctx, ctx.BlockHeight()+1, valSetID) + } else { + // the val set change is effective immediately. + k.SetValidatorSetID(ctx, ctx.BlockHeight(), valSetID) + } + return ret +} + +// SetValidator stores a validator based on the pub key derived address. This +// is accessible in the genesis state via `val_set`. +func (k Keeper) SetValidator(ctx sdk.Context, validator types.ExocoreValidator) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&validator) + + store.Set(types.ExocoreValidatorKey(validator.Address), bz) +} + +// GetValidator gets a validator based on the pub key derived address. +func (k Keeper) GetValidator( + ctx sdk.Context, addr []byte, +) (validator types.ExocoreValidator, found bool) { + store := ctx.KVStore(k.storeKey) + v := store.Get(types.ExocoreValidatorKey(addr)) + if v == nil { + return + } + k.cdc.MustUnmarshal(v, &validator) + found = true + + return +} + +// DeleteValidator deletes a validator based on the pub key derived address. +func (k Keeper) DeleteValidator(ctx sdk.Context, addr []byte) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ExocoreValidatorKey(addr)) +} + +// GetAllExocoreValidators returns all validators in the store. +func (k Keeper) GetAllExocoreValidators( + ctx sdk.Context, +) (validators []types.ExocoreValidator) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte{types.ExocoreValidatorBytePrefix}) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + val := types.ExocoreValidator{} + k.cdc.MustUnmarshal(iterator.Value(), &val) + validators = append(validators, val) + } + + return validators +} + +// GetHistoricalInfo gets the historical info at a given height. It is part of the +// implementation of the staking keeper expected by IBC. +func (k Keeper) GetHistoricalInfo( + ctx sdk.Context, height int64, +) (stakingtypes.HistoricalInfo, bool) { + headerSubset, found := k.GetBlockHeader(ctx, height) + if !found { + // only panic in the case of an unmarshal error + return stakingtypes.HistoricalInfo{}, false + } + valSetID, found := k.GetValidatorSetID(ctx, height) + if !found { + // only panic in the case of an unmarshal error + return stakingtypes.HistoricalInfo{}, false + } + valSet, found := k.GetValidatorSet(ctx, valSetID) + if !found { + // only panic in the case of an unmarshal error + return stakingtypes.HistoricalInfo{}, false + } + header := tmproto.Header{ + Time: headerSubset.Time, + NextValidatorsHash: headerSubset.NextValidatorsHash, + AppHash: headerSubset.AppHash, + } + return stakingtypes.NewHistoricalInfo( + header, stakingtypes.Validators(valSet.GetList()), sdk.DefaultPowerReduction, + ), true +} + +// SetValidatorSet sets the validator set at a given id. This is +// (intentionally) not exported in the genesis state. +func (k Keeper) SetValidatorSet( + ctx sdk.Context, id uint64, vs *types.Validators, +) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetKey(id) + value := k.cdc.MustMarshal(vs) + store.Set(key, value) +} + +// GetValidatorSet gets the validator set at a given id. +func (k Keeper) GetValidatorSet( + ctx sdk.Context, id uint64, +) (*types.Validators, bool) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetKey(id) + if !store.Has(key) { + return nil, false + } + value := store.Get(key) + var hi types.Validators + k.cdc.MustUnmarshal(value, &hi) + return &hi, true +} + +// DeleteValidatorSet deletes the validator set at a given id. +func (k Keeper) DeleteValidatorSet(ctx sdk.Context, id uint64) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetKey(id) + store.Delete(key) +} + +// TrackHistoricalInfo saves the latest historical info and deletes the ones eligible to be +// pruned. The historical info is stored in two parts: one is the header and the other is the +// validator set. Within an epoch, the validator set will only change if there is a slashing +// event. Otherwise, it is constant. The header, however, will change at every block. Since +// the Cosmos SDK does not allow for the retrieval of a past block header, we store the header +// ourselves in this function. The validator set is stored when it changes at the end of an +// epoch or at a slashing event in the corresponding functions. +func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) { + // Get the number of historical entries to persist, as the number of block heights. + numHistoricalEntries := k.GetHistoricalEntries(ctx) + + // we are deleting headers, say, from, 0 to 999 at block 1999 + // for these headers, we must find the corresponding validator set ids to delete. + // they must be only deleted if no other block is using them. + lastDeletedID := uint64(0) // contract: starts from 1. + for i := ctx.BlockHeight() - int64(numHistoricalEntries); i >= 0; i-- { + _, found := k.GetBlockHeader(ctx, i) + if found { + // because they are deleted together, and saved one after the other, + // since the block header exists, so must the validator set id. + lastDeletedID, _ = k.GetValidatorSetID(ctx, i+1) + // clear both the header and the mapping + k.DeleteBlockHeader(ctx, i) + k.DeleteValidatorSetID(ctx, i) + } else { + break + } + } + currentID, _ := k.GetValidatorSetID( + ctx, ctx.BlockHeight()-int64(numHistoricalEntries)+1, + ) + for i := lastDeletedID; i < currentID; i++ { + k.DeleteValidatorSet(ctx, i) + } + + // if there is no need to persist historicalInfo, return. + if numHistoricalEntries == 0 { + return + } + + // store the header + k.StoreBlockHeader(ctx) + + // we have stored: + // before TrackHistoricalInfo: ValidatorSetID for height, and the validator set. + // within TrackHistoricalInfo: the header. + // this is enough information to answer the GetHistoricalInfo query. +} + +// MustGetCurrentValidatorsAsABCIUpdates gets all validators converted +// to the ABCI validator update type. It panics in case of failure. +func (k Keeper) MustGetCurrentValidatorsAsABCIUpdates(ctx sdk.Context) []abci.ValidatorUpdate { + vals := k.GetAllExocoreValidators(ctx) + valUpdates := make([]abci.ValidatorUpdate, 0, len(vals)) + for _, v := range vals { + pk, err := v.ConsPubKey() + if err != nil { + // This should never happen as the pubkey is assumed + // to be stored correctly earlier. + panic(err) + } + tmPK, err := cryptocodec.ToTmProtoPublicKey(pk) + if err != nil { + // This should never happen as the pubkey is assumed + // to be stored correctly earlier. + panic(err) + } + valUpdates = append(valUpdates, abci.ValidatorUpdate{PubKey: tmPK, Power: v.Power}) + } + return valUpdates +} + +// GetValidatorSetID returns the identifier of the validator set at a given height. +// It is used to "share" the validator set entries across multiple heights within an epoch. +// Typically, the validator set should change only at the end of an epoch. However, in the +// case of a slashing occurrence, the validator set may change within an epoch. +func (k Keeper) GetValidatorSetID(ctx sdk.Context, height int64) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetIDKey(height) + value := store.Get(key) + if value == nil { + return 0, false + } + return sdk.BigEndianToUint64(value), true +} + +// SetValidatorSetID sets the identifier of the validator set at a given height. +func (k Keeper) SetValidatorSetID(ctx sdk.Context, height int64, id uint64) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetIDKey(height) + value := sdk.Uint64ToBigEndian(id) + store.Set(key, value) +} + +// DeleteValidatorSetID deletes the identifier of the validator set at a given height. +func (k Keeper) DeleteValidatorSetID(ctx sdk.Context, height int64) { + store := ctx.KVStore(k.storeKey) + key := types.ValidatorSetIDKey(height) + store.Delete(key) +} + +// GetBlockHeader returns the block header at a given height. +func (k Keeper) GetBlockHeader(ctx sdk.Context, height int64) (types.HeaderSubset, bool) { + store := ctx.KVStore(k.storeKey) + key := types.HeaderKey(height) + var header types.HeaderSubset + value := store.Get(key) + if value == nil { + return header, false + } + k.cdc.MustUnmarshal(value, &header) + return header, true +} + +// SetBlockHeader sets the block header at a given height. +func (k Keeper) DeleteBlockHeader(ctx sdk.Context, height int64) { + store := ctx.KVStore(k.storeKey) + key := types.HeaderKey(height) + store.Delete(key) +} + +// StoreBlockHeader stores the block header subset as of the current height. +func (k Keeper) StoreBlockHeader(ctx sdk.Context) { + key := types.HeaderKey(ctx.BlockHeight()) + sdkHeader := ctx.BlockHeader() + header := types.HeaderSubset{ + Time: sdkHeader.Time, + NextValidatorsHash: sdkHeader.NextValidatorsHash, + AppHash: sdkHeader.GetAppHash(), + } + store := ctx.KVStore(k.storeKey) + value := k.cdc.MustMarshal(&header) + store.Set(key, value) +} diff --git a/x/dogfood/module.go b/x/dogfood/module.go index e8ea80431..50d02a5d0 100644 --- a/x/dogfood/module.go +++ b/x/dogfood/module.go @@ -141,14 +141,12 @@ func (am AppModule) InitGenesis( // Initialize global index to index in genesis state cdc.MustUnmarshalJSON(gs, &genState) - InitGenesis(ctx, am.keeper, genState) - - return []abci.ValidatorUpdate{} + return am.keeper.InitGenesis(ctx, genState) } // ExportGenesis returns the module's exported genesis state as raw JSON bytes. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - genState := ExportGenesis(ctx, am.keeper) + genState := am.keeper.ExportGenesis(ctx) return cdc.MustMarshalJSON(genState) } @@ -161,6 +159,6 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} // EndBlock contains the logic that is automatically triggered at the end of each block -func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - return []abci.ValidatorUpdate{} +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.keeper.EndBlock(ctx) } diff --git a/x/dogfood/types/dogfood.pb.go b/x/dogfood/types/dogfood.pb.go new file mode 100644 index 000000000..fc3868a19 --- /dev/null +++ b/x/dogfood/types/dogfood.pb.go @@ -0,0 +1,1984 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: exocore/dogfood/v1/dogfood.proto + +package types + +import ( + fmt "fmt" + crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/x/staking/types" + _ "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/timestamppb" + 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. +// 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 + +// OperationType is used to indicate the type of operation that is being +// cached by the module to create the updated validator set. +type OperationType int32 + +const ( + // KeyOpUnspecified is used to indicate that the operation type is not specified. + // This should never be used. + KeyOpUnspecified OperationType = 0 + // KeyAddition is used to indicate that the operation is a key addition. + KeyAdditionOrUpdate OperationType = 1 + // KeyRemoval is used to indicate that the operation is a key removal. Typically + // this is done due to key replacement mechanism and not directly. + KeyRemoval OperationType = 2 +) + +var OperationType_name = map[int32]string{ + 0: "OPERATION_TYPE_UNSPECIFIED", + 1: "OPERATION_TYPE_ADDITION_OR_UPDATE", + 2: "OPERATION_TYPE_REMOVAL", +} + +var OperationType_value = map[string]int32{ + "OPERATION_TYPE_UNSPECIFIED": 0, + "OPERATION_TYPE_ADDITION_OR_UPDATE": 1, + "OPERATION_TYPE_REMOVAL": 2, +} + +func (x OperationType) String() string { + return proto.EnumName(OperationType_name, int32(x)) +} + +func (OperationType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{0} +} + +// QueueResultType is used to indicate the result of the queue operation. +type QueueResultType int32 + +const ( + // QueueResultUnspecified is used to indicate that the queue result type is not specified. + QueueResultUnspecified QueueResultType = 0 + // QueueResultSuccess is used to indicate that the queue operation was successful. + QueueResultSuccess QueueResultType = 1 + // QueueResultExists is used to indicate that the queue operation failed because the + // operation already exists in the queue. + QueueResultExists QueueResultType = 2 + // QueueResultRemoved is used to indicate that the queue operation resulted in an existing + // operation being removed from the queue. + QueueResultRemoved QueueResultType = 3 +) + +var QueueResultType_name = map[int32]string{ + 0: "QUEUE_RESULT_TYPE_UNSPECIFIED", + 1: "QUEUE_RESULT_TYPE_SUCCESS", + 2: "QUEUE_RESULT_TYPE_EXISTS", + 3: "QUEUE_RESULT_TYPE_REMOVED", +} + +var QueueResultType_value = map[string]int32{ + "QUEUE_RESULT_TYPE_UNSPECIFIED": 0, + "QUEUE_RESULT_TYPE_SUCCESS": 1, + "QUEUE_RESULT_TYPE_EXISTS": 2, + "QUEUE_RESULT_TYPE_REMOVED": 3, +} + +func (x QueueResultType) String() string { + return proto.EnumName(QueueResultType_name, int32(x)) +} + +func (QueueResultType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{1} +} + +// ExocoreValidator is a validator that is part of the Exocore network. It is +// used to validate and sign blocks and transactions. +type ExocoreValidator struct { + // The address, as derived from the consensus key. It has no relation + // with the operator's account address. + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // Last known power + Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` + // pubkey is the consensus public key of the validator, as a Protobuf Any. + Pubkey *types.Any `protobuf:"bytes,3,opt,name=pubkey,proto3" json:"pubkey,omitempty" yaml:"consensus_pubkey"` +} + +func (m *ExocoreValidator) Reset() { *m = ExocoreValidator{} } +func (m *ExocoreValidator) String() string { return proto.CompactTextString(m) } +func (*ExocoreValidator) ProtoMessage() {} +func (*ExocoreValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{0} +} +func (m *ExocoreValidator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExocoreValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExocoreValidator.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 *ExocoreValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExocoreValidator.Merge(m, src) +} +func (m *ExocoreValidator) XXX_Size() int { + return m.Size() +} +func (m *ExocoreValidator) XXX_DiscardUnknown() { + xxx_messageInfo_ExocoreValidator.DiscardUnknown(m) +} + +var xxx_messageInfo_ExocoreValidator proto.InternalMessageInfo + +func (m *ExocoreValidator) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +func (m *ExocoreValidator) GetPower() int64 { + if m != nil { + return m.Power + } + return 0 +} + +func (m *ExocoreValidator) GetPubkey() *types.Any { + if m != nil { + return m.Pubkey + } + return nil +} + +// Operation is used to indicate the operation that is being cached by the module +// to create the updated validator set. +type Operation struct { + // OperationType is the type of the operation (addition / removal). + OperationType OperationType `protobuf:"varint,1,opt,name=operation_type,json=operationType,proto3,enum=exocore.dogfood.v1.OperationType" json:"operation_type,omitempty"` + // OperatorAddress is the sdk.AccAddress of the operator. + OperatorAddress []byte `protobuf:"bytes,2,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty"` + // PubKey is the public key for which the operation is being applied. + PubKey crypto.PublicKey `protobuf:"bytes,3,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` +} + +func (m *Operation) Reset() { *m = Operation{} } +func (m *Operation) String() string { return proto.CompactTextString(m) } +func (*Operation) ProtoMessage() {} +func (*Operation) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{1} +} +func (m *Operation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Operation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Operation.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 *Operation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Operation.Merge(m, src) +} +func (m *Operation) XXX_Size() int { + return m.Size() +} +func (m *Operation) XXX_DiscardUnknown() { + xxx_messageInfo_Operation.DiscardUnknown(m) +} + +var xxx_messageInfo_Operation proto.InternalMessageInfo + +func (m *Operation) GetOperationType() OperationType { + if m != nil { + return m.OperationType + } + return KeyOpUnspecified +} + +func (m *Operation) GetOperatorAddress() []byte { + if m != nil { + return m.OperatorAddress + } + return nil +} + +func (m *Operation) GetPubKey() crypto.PublicKey { + if m != nil { + return m.PubKey + } + return crypto.PublicKey{} +} + +// Operations is a collection of Operation. +type Operations struct { + List []Operation `protobuf:"bytes,1,rep,name=list,proto3" json:"list"` +} + +func (m *Operations) Reset() { *m = Operations{} } +func (m *Operations) String() string { return proto.CompactTextString(m) } +func (*Operations) ProtoMessage() {} +func (*Operations) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{2} +} +func (m *Operations) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Operations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Operations.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 *Operations) XXX_Merge(src proto.Message) { + xxx_messageInfo_Operations.Merge(m, src) +} +func (m *Operations) XXX_Size() int { + return m.Size() +} +func (m *Operations) XXX_DiscardUnknown() { + xxx_messageInfo_Operations.DiscardUnknown(m) +} + +var xxx_messageInfo_Operations proto.InternalMessageInfo + +func (m *Operations) GetList() []Operation { + if m != nil { + return m.List + } + return nil +} + +// AccountAddresses represents a list of account addresses. It is used to store the list of +// operator addresses whose operations are maturing at an epoch. +type AccountAddresses struct { + List [][]byte `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` +} + +func (m *AccountAddresses) Reset() { *m = AccountAddresses{} } +func (m *AccountAddresses) String() string { return proto.CompactTextString(m) } +func (*AccountAddresses) ProtoMessage() {} +func (*AccountAddresses) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{3} +} +func (m *AccountAddresses) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccountAddresses) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccountAddresses.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 *AccountAddresses) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccountAddresses.Merge(m, src) +} +func (m *AccountAddresses) XXX_Size() int { + return m.Size() +} +func (m *AccountAddresses) XXX_DiscardUnknown() { + xxx_messageInfo_AccountAddresses.DiscardUnknown(m) +} + +var xxx_messageInfo_AccountAddresses proto.InternalMessageInfo + +func (m *AccountAddresses) GetList() [][]byte { + if m != nil { + return m.List + } + return nil +} + +// ConsensusAddresses represents a list of account addresses. It is used to store the list of +// addresses (which correspond to operator public keys) to delete at the end of an epoch. +type ConsensusAddresses struct { + List [][]byte `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` +} + +func (m *ConsensusAddresses) Reset() { *m = ConsensusAddresses{} } +func (m *ConsensusAddresses) String() string { return proto.CompactTextString(m) } +func (*ConsensusAddresses) ProtoMessage() {} +func (*ConsensusAddresses) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{4} +} +func (m *ConsensusAddresses) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusAddresses) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusAddresses.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 *ConsensusAddresses) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusAddresses.Merge(m, src) +} +func (m *ConsensusAddresses) XXX_Size() int { + return m.Size() +} +func (m *ConsensusAddresses) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusAddresses.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusAddresses proto.InternalMessageInfo + +func (m *ConsensusAddresses) GetList() [][]byte { + if m != nil { + return m.List + } + return nil +} + +// UndelegationRecordKeys is a collection of undelegation record keys. This is used to store a +// list of undelegation records to mature in the delegation module at the end of the epoch. +type UndelegationRecordKeys struct { + List [][]byte `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` +} + +func (m *UndelegationRecordKeys) Reset() { *m = UndelegationRecordKeys{} } +func (m *UndelegationRecordKeys) String() string { return proto.CompactTextString(m) } +func (*UndelegationRecordKeys) ProtoMessage() {} +func (*UndelegationRecordKeys) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{5} +} +func (m *UndelegationRecordKeys) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UndelegationRecordKeys) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UndelegationRecordKeys.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 *UndelegationRecordKeys) XXX_Merge(src proto.Message) { + xxx_messageInfo_UndelegationRecordKeys.Merge(m, src) +} +func (m *UndelegationRecordKeys) XXX_Size() int { + return m.Size() +} +func (m *UndelegationRecordKeys) XXX_DiscardUnknown() { + xxx_messageInfo_UndelegationRecordKeys.DiscardUnknown(m) +} + +var xxx_messageInfo_UndelegationRecordKeys proto.InternalMessageInfo + +func (m *UndelegationRecordKeys) GetList() [][]byte { + if m != nil { + return m.List + } + return nil +} + +// Validators is a list of validators stored according to the staking module. +type Validators struct { + List []types1.Validator `protobuf:"bytes,1,rep,name=list,proto3" json:"list"` +} + +func (m *Validators) Reset() { *m = Validators{} } +func (m *Validators) String() string { return proto.CompactTextString(m) } +func (*Validators) ProtoMessage() {} +func (*Validators) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{6} +} +func (m *Validators) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Validators) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Validators.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 *Validators) XXX_Merge(src proto.Message) { + xxx_messageInfo_Validators.Merge(m, src) +} +func (m *Validators) XXX_Size() int { + return m.Size() +} +func (m *Validators) XXX_DiscardUnknown() { + xxx_messageInfo_Validators.DiscardUnknown(m) +} + +var xxx_messageInfo_Validators proto.InternalMessageInfo + +func (m *Validators) GetList() []types1.Validator { + if m != nil { + return m.List + } + return nil +} + +// HeaderSubset is a subset of the block header that is relevant to the IBC codebase. It is +// stored for each height and then converted to the `tm.Header` object after queried. It is +// pruned when the information is no longer needed according to the `HistoricalEntries` param. +type HeaderSubset struct { + // timestamp of the block + Time time.Time `protobuf:"bytes,1,opt,name=time,proto3,stdtime" json:"time"` + // validators for the next block + NextValidatorsHash []byte `protobuf:"bytes,2,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"` + // state after txs from the previous block + AppHash []byte `protobuf:"bytes,3,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` +} + +func (m *HeaderSubset) Reset() { *m = HeaderSubset{} } +func (m *HeaderSubset) String() string { return proto.CompactTextString(m) } +func (*HeaderSubset) ProtoMessage() {} +func (*HeaderSubset) Descriptor() ([]byte, []int) { + return fileDescriptor_071b9989c501c3f2, []int{7} +} +func (m *HeaderSubset) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeaderSubset) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeaderSubset.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 *HeaderSubset) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeaderSubset.Merge(m, src) +} +func (m *HeaderSubset) XXX_Size() int { + return m.Size() +} +func (m *HeaderSubset) XXX_DiscardUnknown() { + xxx_messageInfo_HeaderSubset.DiscardUnknown(m) +} + +var xxx_messageInfo_HeaderSubset proto.InternalMessageInfo + +func (m *HeaderSubset) GetTime() time.Time { + if m != nil { + return m.Time + } + return time.Time{} +} + +func (m *HeaderSubset) GetNextValidatorsHash() []byte { + if m != nil { + return m.NextValidatorsHash + } + return nil +} + +func (m *HeaderSubset) GetAppHash() []byte { + if m != nil { + return m.AppHash + } + return nil +} + +func init() { + proto.RegisterEnum("exocore.dogfood.v1.OperationType", OperationType_name, OperationType_value) + proto.RegisterEnum("exocore.dogfood.v1.QueueResultType", QueueResultType_name, QueueResultType_value) + proto.RegisterType((*ExocoreValidator)(nil), "exocore.dogfood.v1.ExocoreValidator") + proto.RegisterType((*Operation)(nil), "exocore.dogfood.v1.Operation") + proto.RegisterType((*Operations)(nil), "exocore.dogfood.v1.Operations") + proto.RegisterType((*AccountAddresses)(nil), "exocore.dogfood.v1.AccountAddresses") + proto.RegisterType((*ConsensusAddresses)(nil), "exocore.dogfood.v1.ConsensusAddresses") + proto.RegisterType((*UndelegationRecordKeys)(nil), "exocore.dogfood.v1.UndelegationRecordKeys") + proto.RegisterType((*Validators)(nil), "exocore.dogfood.v1.Validators") + proto.RegisterType((*HeaderSubset)(nil), "exocore.dogfood.v1.HeaderSubset") +} + +func init() { proto.RegisterFile("exocore/dogfood/v1/dogfood.proto", fileDescriptor_071b9989c501c3f2) } + +var fileDescriptor_071b9989c501c3f2 = []byte{ + // 857 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xcf, 0x6f, 0xe3, 0x44, + 0x14, 0x8e, 0x9b, 0xb2, 0xbb, 0x4c, 0xbb, 0x5d, 0x33, 0x84, 0x6e, 0x6a, 0xed, 0xa6, 0x6e, 0x84, + 0x50, 0xa8, 0xc0, 0x26, 0x2d, 0x08, 0x04, 0x02, 0x29, 0x6d, 0x8c, 0x1a, 0x65, 0x69, 0xb2, 0x76, + 0xbc, 0x02, 0x2e, 0x96, 0x63, 0xbf, 0xa6, 0x56, 0x13, 0xcf, 0xc8, 0x33, 0xce, 0xd6, 0xff, 0x01, + 0xca, 0x69, 0x2f, 0x1c, 0x73, 0x42, 0xfc, 0x07, 0x48, 0x9c, 0xb8, 0xaf, 0x38, 0xed, 0x91, 0xd3, + 0x82, 0xda, 0xff, 0x80, 0x1b, 0x37, 0xe4, 0x5f, 0xa9, 0xb7, 0xa9, 0x7a, 0xf3, 0x9b, 0xf7, 0xbe, + 0x37, 0xdf, 0xe7, 0xf7, 0xcd, 0x43, 0x32, 0x9c, 0x13, 0x87, 0x04, 0xa0, 0xba, 0x64, 0x74, 0x42, + 0x88, 0xab, 0x4e, 0x9b, 0xf9, 0xa7, 0x42, 0x03, 0xc2, 0x09, 0xc6, 0x59, 0x85, 0x92, 0x1f, 0x4f, + 0x9b, 0x52, 0x65, 0x44, 0x46, 0x24, 0x49, 0xab, 0xf1, 0x57, 0x5a, 0x29, 0x6d, 0x8d, 0x08, 0x19, + 0x8d, 0x41, 0x4d, 0xa2, 0x61, 0x78, 0xa2, 0xda, 0x7e, 0x94, 0xa5, 0xb6, 0xaf, 0xa7, 0xb8, 0x37, + 0x01, 0xc6, 0xed, 0x09, 0xcd, 0xb1, 0x0e, 0x61, 0x13, 0xc2, 0xac, 0xb4, 0x69, 0x1a, 0x64, 0xa9, + 0x47, 0x1c, 0x7c, 0x17, 0x82, 0x89, 0xe7, 0x73, 0xd5, 0x09, 0x22, 0xca, 0x89, 0x7a, 0x06, 0x51, + 0x9e, 0x7d, 0x3f, 0xad, 0x55, 0x19, 0xb7, 0xcf, 0x3c, 0x7f, 0xa4, 0x4e, 0x9b, 0x43, 0xe0, 0x76, + 0x33, 0x8f, 0xd3, 0xaa, 0xfa, 0xaf, 0x02, 0x12, 0xb5, 0x54, 0xc7, 0x33, 0x7b, 0xec, 0xb9, 0x36, + 0x27, 0x01, 0xae, 0xa2, 0xbb, 0xb6, 0xeb, 0x06, 0xc0, 0x58, 0x55, 0x90, 0x85, 0xc6, 0xba, 0x9e, + 0x87, 0xb8, 0x82, 0xde, 0xa2, 0xe4, 0x39, 0x04, 0xd5, 0x15, 0x59, 0x68, 0x94, 0xf5, 0x34, 0xc0, + 0x36, 0xba, 0x43, 0xc3, 0xe1, 0x19, 0x44, 0xd5, 0xb2, 0x2c, 0x34, 0xd6, 0xf6, 0x2a, 0x4a, 0xaa, + 0x4a, 0xc9, 0x55, 0x29, 0x2d, 0x3f, 0x3a, 0xd8, 0xff, 0xf7, 0xf5, 0xf6, 0xc3, 0xc8, 0x9e, 0x8c, + 0xbf, 0xac, 0x3b, 0xc4, 0x67, 0xe0, 0xb3, 0x90, 0x59, 0x29, 0xae, 0xfe, 0xe7, 0x6f, 0x1f, 0x57, + 0x32, 0x6d, 0xa9, 0x12, 0xa5, 0x1f, 0x0e, 0xbb, 0x10, 0xe9, 0x59, 0xe3, 0xfa, 0x1f, 0x02, 0x7a, + 0xbb, 0x47, 0x21, 0xb0, 0xb9, 0x47, 0x7c, 0x7c, 0x84, 0x36, 0x48, 0x1e, 0x58, 0x3c, 0xa2, 0x90, + 0xf0, 0xdc, 0xd8, 0xdb, 0x51, 0x96, 0x67, 0xa2, 0x2c, 0x60, 0x83, 0x88, 0x82, 0x7e, 0x9f, 0x14, + 0x43, 0xfc, 0x21, 0x12, 0xd3, 0x03, 0x12, 0x58, 0xb9, 0xe6, 0x95, 0x44, 0xf3, 0x83, 0xfc, 0xbc, + 0x95, 0x69, 0xff, 0x0a, 0xdd, 0xa5, 0xe1, 0xd0, 0xba, 0x92, 0xf9, 0x48, 0xb9, 0x1a, 0x40, 0x81, + 0xf6, 0xd8, 0x73, 0xba, 0x10, 0x1d, 0xac, 0xbe, 0x7c, 0xbd, 0x5d, 0x4a, 0xf8, 0x77, 0x21, 0xaa, + 0x6b, 0x08, 0x2d, 0x78, 0x30, 0xfc, 0x39, 0x5a, 0x1d, 0x7b, 0x8c, 0x57, 0x05, 0xb9, 0xdc, 0x58, + 0xdb, 0x7b, 0x7c, 0x2b, 0xeb, 0xac, 0x51, 0x02, 0xa8, 0x7f, 0x80, 0xc4, 0x96, 0xe3, 0x90, 0xd0, + 0xe7, 0x19, 0x2b, 0x60, 0x18, 0x17, 0x9a, 0xad, 0x67, 0x75, 0x0d, 0x84, 0x0f, 0xf3, 0x7f, 0x7c, + 0x7b, 0xe5, 0x47, 0x68, 0xd3, 0xf4, 0x5d, 0x18, 0xc3, 0x28, 0xb9, 0x4d, 0x07, 0x87, 0x04, 0x6e, + 0x17, 0xa2, 0x9b, 0xab, 0x3b, 0x08, 0x2d, 0x6c, 0x12, 0xff, 0x91, 0xa2, 0x8c, 0x1d, 0x25, 0x9b, + 0x60, 0xee, 0xb0, 0xcc, 0x71, 0xca, 0x02, 0xf1, 0x86, 0x94, 0x9f, 0x05, 0xb4, 0x7e, 0x04, 0xb6, + 0x0b, 0x81, 0x11, 0x0e, 0x19, 0x70, 0xfc, 0x05, 0x5a, 0x8d, 0xcd, 0x9f, 0x8c, 0x72, 0x6d, 0x4f, + 0x5a, 0xf2, 0xd0, 0x20, 0x7f, 0x19, 0x07, 0xf7, 0xe2, 0x36, 0x2f, 0xfe, 0xde, 0x16, 0xf4, 0x04, + 0x81, 0x3f, 0x41, 0x15, 0x1f, 0xce, 0xb9, 0x35, 0x5d, 0x50, 0xb3, 0x4e, 0x6d, 0x76, 0x9a, 0x0d, + 0x12, 0xc7, 0xb9, 0x2b, 0xd6, 0x47, 0x36, 0x3b, 0xc5, 0x5b, 0xe8, 0x9e, 0x4d, 0x69, 0x5a, 0x55, + 0xce, 0x2c, 0x4e, 0x69, 0x9c, 0xda, 0xfd, 0x5d, 0x40, 0xf7, 0xdf, 0xb0, 0x0c, 0xfe, 0x14, 0x49, + 0xbd, 0xbe, 0xa6, 0xb7, 0x06, 0x9d, 0xde, 0xb1, 0x35, 0xf8, 0xa1, 0xaf, 0x59, 0xe6, 0xb1, 0xd1, + 0xd7, 0x0e, 0x3b, 0xdf, 0x76, 0xb4, 0xb6, 0x58, 0x92, 0x2a, 0xb3, 0xb9, 0x2c, 0x76, 0x21, 0xea, + 0x51, 0xd3, 0x67, 0x14, 0x1c, 0xef, 0xc4, 0x03, 0x17, 0x7f, 0x83, 0x76, 0xae, 0xa1, 0x5a, 0xed, + 0x76, 0x27, 0x89, 0x7a, 0xba, 0x65, 0xf6, 0xdb, 0xad, 0x81, 0x26, 0x0a, 0xd2, 0xc3, 0xd9, 0x5c, + 0x7e, 0xb7, 0x0b, 0x51, 0xcb, 0x75, 0xbd, 0xf8, 0xc6, 0x5e, 0x60, 0x52, 0xd7, 0xe6, 0x80, 0x77, + 0xd1, 0xe6, 0x35, 0xbc, 0xae, 0x7d, 0xd7, 0x7b, 0xd6, 0x7a, 0x22, 0xae, 0x48, 0x1b, 0xb3, 0xb9, + 0x8c, 0xe2, 0xe7, 0x01, 0x13, 0x32, 0xb5, 0xc7, 0xd2, 0xea, 0x4f, 0xbf, 0xd4, 0x4a, 0xbb, 0xff, + 0x09, 0xe8, 0xc1, 0xd3, 0x10, 0x42, 0xd0, 0x81, 0x85, 0x63, 0x9e, 0x70, 0xff, 0x1a, 0x3d, 0x7e, + 0x6a, 0x6a, 0x66, 0x0c, 0x36, 0xcc, 0x27, 0x83, 0x9b, 0xe8, 0x4b, 0xb3, 0xb9, 0xbc, 0x59, 0xc0, + 0x15, 0x45, 0x7c, 0x86, 0xb6, 0x96, 0xe1, 0x86, 0x79, 0x78, 0xa8, 0x19, 0x86, 0x28, 0x48, 0x9b, + 0xb3, 0xb9, 0x8c, 0x0b, 0x50, 0x23, 0x74, 0x9c, 0xf8, 0xa9, 0xec, 0xa3, 0xea, 0x32, 0x4c, 0xfb, + 0xbe, 0x63, 0x0c, 0x0c, 0x71, 0x45, 0x7a, 0x6f, 0x36, 0x97, 0xdf, 0x29, 0xa0, 0xb4, 0x73, 0x8f, + 0x71, 0x76, 0xf3, 0x5d, 0x89, 0x66, 0xad, 0x2d, 0x96, 0x97, 0xee, 0x4a, 0xb4, 0x83, 0x9b, 0x6a, + 0x3f, 0xe8, 0xbe, 0xbc, 0xa8, 0x09, 0xaf, 0x2e, 0x6a, 0xc2, 0x3f, 0x17, 0x35, 0xe1, 0xc5, 0x65, + 0xad, 0xf4, 0xea, 0xb2, 0x56, 0xfa, 0xeb, 0xb2, 0x56, 0xfa, 0xb1, 0x39, 0xf2, 0xf8, 0x69, 0x38, + 0x54, 0x1c, 0x32, 0x51, 0xb3, 0x4d, 0x77, 0x0c, 0xfc, 0x39, 0x09, 0xce, 0xd4, 0x7c, 0xc5, 0x9f, + 0x2f, 0x96, 0x7c, 0xbc, 0x4b, 0xd8, 0xf0, 0x4e, 0xe2, 0xb9, 0xfd, 0xff, 0x03, 0x00, 0x00, 0xff, + 0xff, 0x41, 0xe1, 0x03, 0x2c, 0x04, 0x06, 0x00, 0x00, +} + +func (m *ExocoreValidator) 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 *ExocoreValidator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExocoreValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pubkey != nil { + { + size, err := m.Pubkey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDogfood(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Power != 0 { + i = encodeVarintDogfood(dAtA, i, uint64(m.Power)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Operation) 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 *Operation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Operation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDogfood(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.OperatorAddress) > 0 { + i -= len(m.OperatorAddress) + copy(dAtA[i:], m.OperatorAddress) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.OperatorAddress))) + i-- + dAtA[i] = 0x12 + } + if m.OperationType != 0 { + i = encodeVarintDogfood(dAtA, i, uint64(m.OperationType)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Operations) 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 *Operations) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Operations) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.List) > 0 { + for iNdEx := len(m.List) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.List[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDogfood(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *AccountAddresses) 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 *AccountAddresses) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccountAddresses) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.List) > 0 { + for iNdEx := len(m.List) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.List[iNdEx]) + copy(dAtA[i:], m.List[iNdEx]) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.List[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ConsensusAddresses) 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 *ConsensusAddresses) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusAddresses) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.List) > 0 { + for iNdEx := len(m.List) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.List[iNdEx]) + copy(dAtA[i:], m.List[iNdEx]) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.List[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *UndelegationRecordKeys) 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 *UndelegationRecordKeys) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UndelegationRecordKeys) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.List) > 0 { + for iNdEx := len(m.List) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.List[iNdEx]) + copy(dAtA[i:], m.List[iNdEx]) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.List[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Validators) 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 *Validators) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Validators) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.List) > 0 { + for iNdEx := len(m.List) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.List[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDogfood(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *HeaderSubset) 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 *HeaderSubset) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeaderSubset) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AppHash) > 0 { + i -= len(m.AppHash) + copy(dAtA[i:], m.AppHash) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.AppHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.NextValidatorsHash) > 0 { + i -= len(m.NextValidatorsHash) + copy(dAtA[i:], m.NextValidatorsHash) + i = encodeVarintDogfood(dAtA, i, uint64(len(m.NextValidatorsHash))) + i-- + dAtA[i] = 0x12 + } + n3, err3 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintDogfood(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintDogfood(dAtA []byte, offset int, v uint64) int { + offset -= sovDogfood(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ExocoreValidator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovDogfood(uint64(l)) + } + if m.Power != 0 { + n += 1 + sovDogfood(uint64(m.Power)) + } + if m.Pubkey != nil { + l = m.Pubkey.Size() + n += 1 + l + sovDogfood(uint64(l)) + } + return n +} + +func (m *Operation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OperationType != 0 { + n += 1 + sovDogfood(uint64(m.OperationType)) + } + l = len(m.OperatorAddress) + if l > 0 { + n += 1 + l + sovDogfood(uint64(l)) + } + l = m.PubKey.Size() + n += 1 + l + sovDogfood(uint64(l)) + return n +} + +func (m *Operations) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.List) > 0 { + for _, e := range m.List { + l = e.Size() + n += 1 + l + sovDogfood(uint64(l)) + } + } + return n +} + +func (m *AccountAddresses) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.List) > 0 { + for _, b := range m.List { + l = len(b) + n += 1 + l + sovDogfood(uint64(l)) + } + } + return n +} + +func (m *ConsensusAddresses) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.List) > 0 { + for _, b := range m.List { + l = len(b) + n += 1 + l + sovDogfood(uint64(l)) + } + } + return n +} + +func (m *UndelegationRecordKeys) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.List) > 0 { + for _, b := range m.List { + l = len(b) + n += 1 + l + sovDogfood(uint64(l)) + } + } + return n +} + +func (m *Validators) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.List) > 0 { + for _, e := range m.List { + l = e.Size() + n += 1 + l + sovDogfood(uint64(l)) + } + } + return n +} + +func (m *HeaderSubset) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time) + n += 1 + l + sovDogfood(uint64(l)) + l = len(m.NextValidatorsHash) + if l > 0 { + n += 1 + l + sovDogfood(uint64(l)) + } + l = len(m.AppHash) + if l > 0 { + n += 1 + l + sovDogfood(uint64(l)) + } + return n +} + +func sovDogfood(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDogfood(x uint64) (n int) { + return sovDogfood(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ExocoreValidator) 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 ErrIntOverflowDogfood + } + 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: ExocoreValidator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExocoreValidator: 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 byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = append(m.Address[:0], dAtA[iNdEx:postIndex]...) + if m.Address == nil { + m.Address = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) + } + m.Power = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Power |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pubkey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pubkey == nil { + m.Pubkey = &types.Any{} + } + if err := m.Pubkey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Operation) 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 ErrIntOverflowDogfood + } + 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: Operation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Operation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OperationType", wireType) + } + m.OperationType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OperationType |= OperationType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OperatorAddress = append(m.OperatorAddress[:0], dAtA[iNdEx:postIndex]...) + if m.OperatorAddress == nil { + m.OperatorAddress = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Operations) 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 ErrIntOverflowDogfood + } + 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: Operations: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Operations: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, Operation{}) + if err := m.List[len(m.List)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AccountAddresses) 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 ErrIntOverflowDogfood + } + 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: AccountAddresses: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccountAddresses: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, make([]byte, postIndex-iNdEx)) + copy(m.List[len(m.List)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusAddresses) 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 ErrIntOverflowDogfood + } + 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: ConsensusAddresses: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusAddresses: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, make([]byte, postIndex-iNdEx)) + copy(m.List[len(m.List)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UndelegationRecordKeys) 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 ErrIntOverflowDogfood + } + 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: UndelegationRecordKeys: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UndelegationRecordKeys: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, make([]byte, postIndex-iNdEx)) + copy(m.List[len(m.List)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Validators) 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 ErrIntOverflowDogfood + } + 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: Validators: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Validators: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, types1.Validator{}) + if err := m.List[len(m.List)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HeaderSubset) 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 ErrIntOverflowDogfood + } + 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: HeaderSubset: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HeaderSubset: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Time, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextValidatorsHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextValidatorsHash = append(m.NextValidatorsHash[:0], dAtA[iNdEx:postIndex]...) + if m.NextValidatorsHash == nil { + m.NextValidatorsHash = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDogfood + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDogfood + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDogfood + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppHash = append(m.AppHash[:0], dAtA[iNdEx:postIndex]...) + if m.AppHash == nil { + m.AppHash = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDogfood(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDogfood + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDogfood(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, ErrIntOverflowDogfood + } + 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, ErrIntOverflowDogfood + } + 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, ErrIntOverflowDogfood + } + 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, ErrInvalidLengthDogfood + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDogfood + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthDogfood + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthDogfood = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDogfood = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDogfood = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/dogfood/types/expected_keepers.go b/x/dogfood/types/expected_keepers.go index ab1254f4c..06ca74df4 100644 --- a/x/dogfood/types/expected_keepers.go +++ b/x/dogfood/types/expected_keepers.go @@ -1 +1,83 @@ package types + +import ( + "cosmossdk.io/math" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + epochsTypes "github.com/evmos/evmos/v14/x/epochs/types" +) + +// EpochsKeeper represents the expected keeper interface for the epochs module. +type EpochsKeeper interface { + GetEpochInfo(sdk.Context, string) (epochsTypes.EpochInfo, bool) +} + +// DogfoodHooks represents the event hooks for dogfood module. Ideally, these should +// match those of the staking module but for now it is only a subset of them. The side effects +// of calling the other hooks are not relevant to running the chain, so they can be skipped. +type DogfoodHooks interface { + AfterValidatorBonded( + sdk.Context, sdk.ConsAddress, sdk.ValAddress, + ) error +} + +// OperatorHooks is the interface for the operator module's hooks. The functions are called +// whenever an operator opts in to a Cosmos chain, opts out of a Cosmos chain, or replaces their +// public key with another one. +type OperatorHooks interface { + AfterOperatorOptIn(sdk.Context, sdk.AccAddress, string, tmprotocrypto.PublicKey) + AfterOperatorKeyReplacement( + sdk.Context, sdk.AccAddress, tmprotocrypto.PublicKey, tmprotocrypto.PublicKey, string, + ) + AfterOperatorOptOutInitiated(sdk.Context, sdk.AccAddress, string, tmprotocrypto.PublicKey) +} + +// DelegationHooks represent the event hooks for delegation module. +type DelegationHooks interface { + AfterDelegation(sdk.Context, sdk.AccAddress) + AfterUndelegationStarted(sdk.Context, sdk.AccAddress, []byte) + AfterUndelegationCompleted(sdk.Context, sdk.AccAddress, []byte) +} + +// OperatorKeeper represents the expected keeper interface for the operator module. +type OperatorKeeper interface { + GetOperatorConsKeyForChainId( + sdk.Context, sdk.AccAddress, string, + ) (bool, tmprotocrypto.PublicKey, error) + IsOperatorOptingOutFromChainId( + sdk.Context, sdk.AccAddress, string, + ) bool + CompleteOperatorOptOutFromChainId(sdk.Context, sdk.AccAddress, string) + DeleteOperatorAddressForChainIdAndConsAddr(sdk.Context, string, sdk.ConsAddress) + GetOperatorAddressForChainIdAndConsAddr( + sdk.Context, string, sdk.ConsAddress, + ) (bool, sdk.AccAddress) + IsOperatorJailedForChainId(sdk.Context, sdk.AccAddress, string) bool + Jail(sdk.Context, sdk.ConsAddress, string) +} + +// DelegationKeeper represents the expected keeper interface for the delegation module. +type DelegationKeeper interface { + IncrementUndelegationHoldCount(sdk.Context, []byte) + DecrementUndelegationHoldCount(sdk.Context, []byte) +} + +// EpochsHooks represents the event hooks for the epochs module. +type EpochsHooks interface { + AfterEpochEnd(sdk.Context, string, int64) + BeforeEpochStart(sdk.Context, string, int64) +} + +// RestakingKeeper represents the expected keeper interface for the restaking module. +type RestakingKeeper interface { + GetOperatorAssetValue(sdk.Context, sdk.AccAddress) (int64, error) +} + +// SlashingKeeper represents the expected keeper interface for the (exo-)slashing module. +type SlashingKeeper interface { + SlashWithInfractionReason( + sdk.Context, sdk.AccAddress, int64, + int64, sdk.Dec, stakingtypes.Infraction, + ) math.Int +} diff --git a/x/dogfood/types/genesis.pb.go b/x/dogfood/types/genesis.pb.go index a70ecb578..b3744468b 100644 --- a/x/dogfood/types/genesis.pb.go +++ b/x/dogfood/types/genesis.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + types "github.com/cometbft/cometbft/abci/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" @@ -25,7 +26,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the dogfood module's genesis state. type GenesisState struct { + // Parameters of the module. Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // Validator set, stored by ExocoreValidatorKey. + ValSet []types.ValidatorUpdate `protobuf:"bytes,2,rep,name=val_set,json=valSet,proto3" json:"val_set"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -68,6 +72,13 @@ func (m *GenesisState) GetParams() Params { return Params{} } +func (m *GenesisState) GetValSet() []types.ValidatorUpdate { + if m != nil { + return m.ValSet + } + return nil +} + func init() { proto.RegisterType((*GenesisState)(nil), "exocore.dogfood.v1.GenesisState") } @@ -75,20 +86,25 @@ func init() { func init() { proto.RegisterFile("exocore/dogfood/v1/genesis.proto", fileDescriptor_1a9d908a27866b1b) } var fileDescriptor_1a9d908a27866b1b = []byte{ - // 202 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xad, 0xc8, 0x4f, - 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0xc9, 0x4f, 0x4f, 0xcb, 0xcf, 0x4f, 0xd1, 0x2f, 0x33, 0xd4, 0x4f, - 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x82, 0xaa, - 0xd0, 0x83, 0xaa, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xeb, 0x83, - 0x58, 0x10, 0x95, 0x52, 0xf2, 0x58, 0xcc, 0x2a, 0x48, 0x2c, 0x4a, 0xcc, 0x85, 0x1a, 0xa5, 0xe4, - 0xc1, 0xc5, 0xe3, 0x0e, 0x31, 0x3b, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x82, 0x8b, 0x0d, 0x22, - 0x2f, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xa5, 0x87, 0x69, 0x97, 0x5e, 0x00, 0x58, 0x85, - 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x50, 0xf5, 0x4e, 0xde, 0x27, 0x1e, 0xc9, 0x31, 0x5e, - 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, - 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x98, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, - 0xab, 0xef, 0x0a, 0x31, 0xcd, 0x2f, 0xb5, 0xa4, 0x3c, 0xbf, 0x28, 0x5b, 0x1f, 0xe6, 0xbc, 0x0a, - 0xb8, 0x03, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0xae, 0x33, 0x06, 0x04, 0x00, 0x00, - 0xff, 0xff, 0xda, 0xc5, 0xe8, 0xc9, 0x0c, 0x01, 0x00, 0x00, + // 273 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8f, 0x31, 0x4b, 0xc3, 0x40, + 0x14, 0xc7, 0x73, 0x2a, 0x15, 0x52, 0xa7, 0xe0, 0x50, 0x22, 0x5c, 0x83, 0x53, 0xa7, 0x3b, 0x52, + 0x17, 0x37, 0xa1, 0x20, 0x0e, 0x82, 0x88, 0x45, 0x07, 0x17, 0xb9, 0x24, 0xcf, 0x78, 0x98, 0xe4, + 0x85, 0xcb, 0x33, 0xd6, 0x8f, 0xe0, 0xe6, 0xc7, 0xea, 0xd8, 0xd1, 0x49, 0x24, 0xf9, 0x22, 0xd2, + 0xe4, 0xaa, 0x83, 0xdd, 0x0e, 0xee, 0xc7, 0xfb, 0xff, 0x7e, 0x6e, 0x00, 0x0b, 0x8c, 0xd1, 0x80, + 0x4c, 0x30, 0x7d, 0x44, 0x4c, 0x64, 0x1d, 0xca, 0x14, 0x0a, 0xa8, 0x74, 0x25, 0x4a, 0x83, 0x84, + 0x9e, 0x67, 0x09, 0x61, 0x09, 0x51, 0x87, 0xfe, 0x61, 0x8a, 0x29, 0x76, 0xdf, 0x72, 0xfd, 0xea, + 0x49, 0xff, 0x88, 0xa0, 0x48, 0xc0, 0xe4, 0xba, 0x20, 0xa9, 0xa2, 0x58, 0x4b, 0x7a, 0x2b, 0xc1, + 0x9e, 0xf1, 0xc7, 0x5b, 0x86, 0x4a, 0x65, 0x54, 0x6e, 0x81, 0xe3, 0x77, 0xe6, 0x1e, 0x5c, 0xf4, + 0xcb, 0x73, 0x52, 0x04, 0xde, 0xa9, 0x3b, 0xe8, 0x81, 0x11, 0x0b, 0xd8, 0x64, 0x38, 0xf5, 0xc5, + 0x7f, 0x13, 0x71, 0xdd, 0x11, 0xb3, 0xbd, 0xe5, 0xd7, 0xd8, 0xb9, 0xb1, 0xbc, 0x77, 0xe6, 0xee, + 0xd7, 0x2a, 0x7b, 0xa8, 0x80, 0x46, 0x3b, 0xc1, 0xee, 0x64, 0x38, 0x0d, 0xc4, 0x9f, 0x9a, 0x58, + 0xab, 0x89, 0x3b, 0x95, 0xe9, 0x44, 0x11, 0x9a, 0xdb, 0x32, 0x51, 0x04, 0x9b, 0x03, 0xb5, 0xca, + 0xe6, 0x40, 0xb3, 0xcb, 0x65, 0xc3, 0xd9, 0xaa, 0xe1, 0xec, 0xbb, 0xe1, 0xec, 0xa3, 0xe5, 0xce, + 0xaa, 0xe5, 0xce, 0x67, 0xcb, 0x9d, 0xfb, 0x30, 0xd5, 0xf4, 0xf4, 0x12, 0x89, 0x18, 0x73, 0x79, + 0xde, 0xeb, 0x5c, 0x01, 0xbd, 0xa2, 0x79, 0x96, 0x9b, 0xc0, 0xc5, 0x6f, 0x62, 0xd7, 0x1f, 0x0d, + 0xba, 0xbe, 0x93, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x26, 0x56, 0x05, 0x51, 0x6b, 0x01, 0x00, + 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -111,6 +127,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ValSet) > 0 { + for iNdEx := len(m.ValSet) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValSet[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } { size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -143,6 +173,12 @@ func (m *GenesisState) Size() (n int) { _ = l l = m.Params.Size() n += 1 + l + sovGenesis(uint64(l)) + if len(m.ValSet) > 0 { + for _, e := range m.ValSet { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -214,6 +250,40 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValSet", 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.ValSet = append(m.ValSet, types.ValidatorUpdate{}) + if err := m.ValSet[len(m.ValSet)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/dogfood/types/keys.go b/x/dogfood/types/keys.go index 1256e87e4..6397e1796 100644 --- a/x/dogfood/types/keys.go +++ b/x/dogfood/types/keys.go @@ -1,5 +1,9 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( // ModuleName defines the module name ModuleName = "dogfood" @@ -7,3 +11,131 @@ const ( // StoreKey defines the primary module store key StoreKey = ModuleName ) + +// InitialValidatorSetID is the initial validator set id. +const InitialValidatorSetID = uint64(1) + +const ( + // ExocoreValidatorBytePrefix is the prefix for the validator store. + ExocoreValidatorBytePrefix byte = iota + 1 + + // QueuedOperationsByte is the byte used to store the queue of operations. + QueuedOperationsByte + + // OptOutsToFinishBytePrefix is the byte used to store the list of operator addresses whose + // opt outs are maturing at the provided epoch. + OptOutsToFinishBytePrefix + + // OperatorOptOutFinishEpochBytePrefix is the byte prefix to store the epoch at which an + // operator's opt out will mature. + OperatorOptOutFinishEpochBytePrefix + + // ConsensusAddrsToPruneBytePrefix is the byte prefix to store the list of consensus + // addresses that can be pruned from the operator module at the provided epoch. + ConsensusAddrsToPruneBytePrefix + + // UnbondingReleaseMaturityBytePrefix is the byte prefix to store the list of undelegations + // that will mature at the provided epoch. + UnbondingReleaseMaturityBytePrefix + + // PendingOperationsByte is the byte used to store the list of operations to be applied at + // the end of the current block. + PendingOperationsByte + + // PendingOptOutsByte is the byte used to store the list of operator addresses whose opt + // outs will be made effective at the end of the current block. + PendingOptOutsByte + + // PendingConsensusAddrsByte is the byte used to store the list of consensus addresses to be + // pruned at the end of the block. + PendingConsensusAddrsByte + + // PendingUndelegationsByte is the byte used to store the list of undelegations that will + // mature at the end of the current block. + PendingUndelegationsByte + // ValidatorSetBytePrefix is the prefix for the historical validator set store. + ValidatorSetBytePrefix + + // ValidatorSetIDBytePrefix is the prefix for the validator set id store. + ValidatorSetIDBytePrefix + + // HeaderBytePrefix is the prefix for the header store. + HeaderBytePrefix +) + +// ExocoreValidatorKey returns the key for the validator store. +func ExocoreValidatorKey(address sdk.AccAddress) []byte { + return append([]byte{ExocoreValidatorBytePrefix}, address.Bytes()...) +} + +// ValidatorSetKey returns the key for the historical validator set store. +func ValidatorSetKey(id uint64) []byte { + bz := sdk.Uint64ToBigEndian(id) + return append([]byte{ValidatorSetBytePrefix}, bz...) +} + +// ValidatorSetIDKey returns the key for the validator set id store. +func ValidatorSetIDKey(height int64) []byte { + bz := sdk.Uint64ToBigEndian(uint64(height)) + return append([]byte{ValidatorSetIDBytePrefix}, bz...) +} + +// HeaderKey returns the key for the header store. +func HeaderKey(height int64) []byte { + bz := sdk.Uint64ToBigEndian(uint64(height)) + return append([]byte{HeaderBytePrefix}, bz...) +} + +// QueuedOperationsKey returns the key for the queued operations store. +func QueuedOperationsKey() []byte { + return []byte{QueuedOperationsByte} +} + +// OptOutsToFinishKey returns the key for the operator opt out maturity store (epoch -> list of +// addresses). +func OptOutsToFinishKey(epoch int64) []byte { + return append([]byte{OptOutsToFinishBytePrefix}, sdk.Uint64ToBigEndian(uint64(epoch))...) +} + +// OperatorOptOutFinishEpochKey is the key for the operator opt out maturity store +// (sdk.AccAddress -> epoch) +func OperatorOptOutFinishEpochKey(address sdk.AccAddress) []byte { + return append([]byte{OperatorOptOutFinishEpochBytePrefix}, address.Bytes()...) +} + +// ConsensusAddrsToPruneKey is the key to lookup the list of operator consensus addresses that +// can be pruned from the operator module at the provided epoch. +func ConsensusAddrsToPruneKey(epoch int64) []byte { + return append( + []byte{ConsensusAddrsToPruneBytePrefix}, + sdk.Uint64ToBigEndian(uint64(epoch))...) +} + +// UnbondingReleaseMaturityKey is the key to lookup the list of undelegations that will mature +// at the provided epoch. +func UnbondingReleaseMaturityKey(epoch int64) []byte { + return append( + []byte{UnbondingReleaseMaturityBytePrefix}, + sdk.Uint64ToBigEndian(uint64(epoch))...) +} + +// PendingOperationsKey returns the key for the pending operations store. +func PendingOperationsKey() []byte { + return []byte{PendingOperationsByte} +} + +// PendingOptOutsKey returns the key for the pending opt-outs store. +func PendingOptOutsKey() []byte { + return []byte{PendingOptOutsByte} +} + +// PendingConsensusAddrsByte is the byte used to store the list of consensus addresses to be +// pruned at the end of the block. +func PendingConsensusAddrsKey() []byte { + return []byte{PendingConsensusAddrsByte} +} + +// PendingUndelegationsKey returns the key for the pending undelegations store. +func PendingUndelegationsKey() []byte { + return []byte{PendingUndelegationsByte} +} diff --git a/x/dogfood/types/params.go b/x/dogfood/types/params.go index 357196ad6..97fb4f583 100644 --- a/x/dogfood/types/params.go +++ b/x/dogfood/types/params.go @@ -1,38 +1,127 @@ package types import ( + "fmt" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "gopkg.in/yaml.v2" + + epochTypes "github.com/evmos/evmos/v14/x/epochs/types" ) var _ paramtypes.ParamSet = (*Params)(nil) -// ParamKeyTable the param key table for launch module +const ( + // DefaultEpochsUntilUnbonded is the default number of epochs after which an unbonding entry + // is released. For example, if an unbonding is requested during epoch 8, it is made + // effective at the beginning of epoch 9. The unbonding amount is released at the beginning + // of epoch 16 (9 + DefaultEpochsUntilUnbonded). + DefaultEpochsUntilUnbonded = 7 + // DefaultEpochIdentifier is the epoch identifier which is used, by default, to identify the + // epoch. Note that the options include week, day or hour. + DefaultEpochIdentifier = epochTypes.HourEpochID + // DefaultMaxValidators is the default maximum number of bonded validators. + DefaultMaxValidators = stakingtypes.DefaultMaxValidators + // DefaultHistorical entries is the number of entries of historical staking data to persist. + // Apps that don't use IBC can ignore this value by not adding the staking module to the + // application module manager's SetOrderBeginBlockers. + DefaultHistoricalEntries = stakingtypes.DefaultHistoricalEntries +) + +// Reflection based keys for params subspace. +var ( + KeyEpochsUntilUnbonded = []byte("EpochsUntilUnbonded") + KeyEpochIdentifier = []byte("EpochIdentifier") + KeyMaxValidators = []byte("MaxValidators") + KeyHistoricalEntries = []byte("HistoricalEntries") +) + +// ParamKeyTable returns a key table with the necessary registered params. func ParamKeyTable() paramtypes.KeyTable { return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) } -// NewParams creates a new Params instance -func NewParams() Params { - return Params{} +// NewParams creates a new Params instance. +func NewParams( + epochsUntilUnbonded uint32, + epochIdentifier string, + maxValidators uint32, + historicalEntries uint32, +) Params { + return Params{ + EpochsUntilUnbonded: epochsUntilUnbonded, + EpochIdentifier: epochIdentifier, + MaxValidators: maxValidators, + HistoricalEntries: historicalEntries, + } } -// DefaultParams returns a default set of parameters +// DefaultParams returns a default set of parameters. func DefaultParams() Params { - return NewParams() + return NewParams( + DefaultEpochsUntilUnbonded, + DefaultEpochIdentifier, + DefaultMaxValidators, + DefaultHistoricalEntries, + ) } -// ParamSetPairs get the params.ParamSet +// ParamSetPairs implements params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { - return paramtypes.ParamSetPairs{} + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair( + KeyEpochsUntilUnbonded, + &p.EpochsUntilUnbonded, + ValidatePositiveUint32, + ), + paramtypes.NewParamSetPair( + KeyEpochIdentifier, + &p.EpochIdentifier, + epochTypes.ValidateEpochIdentifierInterface, + ), + paramtypes.NewParamSetPair( + KeyMaxValidators, + &p.MaxValidators, + ValidatePositiveUint32, + ), + paramtypes.NewParamSetPair( + KeyHistoricalEntries, + &p.HistoricalEntries, + ValidatePositiveUint32, + ), + } } -// Validate validates the set of params +// Validate validates the set of params. func (p Params) Validate() error { + if err := ValidatePositiveUint32(p.EpochsUntilUnbonded); err != nil { + return fmt.Errorf("epochs until unbonded: %w", err) + } + if err := epochTypes.ValidateEpochIdentifierInterface(p.EpochIdentifier); err != nil { + return fmt.Errorf("epoch identifier: %w", err) + } + if err := ValidatePositiveUint32(p.MaxValidators); err != nil { + return fmt.Errorf("max validators: %w", err) + } + if err := ValidatePositiveUint32(p.HistoricalEntries); err != nil { + return fmt.Errorf("historical entries: %w", err) + } + return nil +} + +// ValidatePositiveUint32 checks whether the supplied value is a positive uint32. +func ValidatePositiveUint32(i interface{}) error { + if val, ok := i.(uint32); !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } else if val == 0 { + return fmt.Errorf("invalid parameter value: %d", val) + } return nil } -// String implements the Stringer interface. +// String implements the Stringer interface. Ths interface is required as part of the +// proto.Message interface, which is used in the query server. func (p Params) String() string { out, _ := yaml.Marshal(p) return string(out) diff --git a/x/dogfood/types/params.pb.go b/x/dogfood/types/params.pb.go index 60a1da67d..1b35e818a 100644 --- a/x/dogfood/types/params.pb.go +++ b/x/dogfood/types/params.pb.go @@ -25,6 +25,17 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Params defines the parameters for the module. type Params struct { + // EpochsUntilUnbonded is the number of epochs after which an unbonding + // is released. Note that it starts from the beginning of the next epoch + // in which the unbonding request was received. At that point, the vote + // power is reduced by the amount of the unbonding operation. + EpochsUntilUnbonded uint32 `protobuf:"varint,1,opt,name=epochs_until_unbonded,json=epochsUntilUnbonded,proto3" json:"epochs_until_unbonded,omitempty"` + // EpochIdentifier is the identifier of the epoch (week, hour, day). + EpochIdentifier string `protobuf:"bytes,2,opt,name=epoch_identifier,json=epochIdentifier,proto3" json:"epoch_identifier,omitempty"` + // MaxValidators is the maximum number of validators. + MaxValidators uint32 `protobuf:"varint,3,opt,name=max_validators,json=maxValidators,proto3" json:"max_validators,omitempty"` + // HistoricalEntries is the number of historical entries to persist. + HistoricalEntries uint32 `protobuf:"varint,4,opt,name=historical_entries,json=historicalEntries,proto3" json:"historical_entries,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -59,6 +70,34 @@ func (m *Params) XXX_DiscardUnknown() { var xxx_messageInfo_Params proto.InternalMessageInfo +func (m *Params) GetEpochsUntilUnbonded() uint32 { + if m != nil { + return m.EpochsUntilUnbonded + } + return 0 +} + +func (m *Params) GetEpochIdentifier() string { + if m != nil { + return m.EpochIdentifier + } + return "" +} + +func (m *Params) GetMaxValidators() uint32 { + if m != nil { + return m.MaxValidators + } + return 0 +} + +func (m *Params) GetHistoricalEntries() uint32 { + if m != nil { + return m.HistoricalEntries + } + return 0 +} + func init() { proto.RegisterType((*Params)(nil), "exocore.dogfood.v1.Params") } @@ -66,17 +105,26 @@ func init() { func init() { proto.RegisterFile("exocore/dogfood/v1/params.proto", fileDescriptor_e8747fb70c97d97f) } var fileDescriptor_e8747fb70c97d97f = []byte{ - // 160 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xad, 0xc8, 0x4f, - 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0xc9, 0x4f, 0x4f, 0xcb, 0xcf, 0x4f, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, - 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x82, 0x2a, 0xd0, - 0x83, 0x2a, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xeb, 0x83, 0x58, - 0x10, 0x95, 0x4a, 0x7c, 0x5c, 0x6c, 0x01, 0x60, 0x9d, 0x56, 0x2c, 0x33, 0x16, 0xc8, 0x33, 0x38, - 0x79, 0x9f, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, - 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x61, 0x7a, 0x66, 0x49, - 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0xbe, 0x2b, 0xc4, 0x78, 0xbf, 0xd4, 0x92, 0xf2, 0xfc, - 0xa2, 0x6c, 0x7d, 0x98, 0x73, 0x2a, 0xe0, 0x0e, 0x2a, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, - 0xdb, 0x61, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x0e, 0x0c, 0x9a, 0xb0, 0x00, 0x00, 0x00, + // 290 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0x3f, 0x4b, 0xc3, 0x40, + 0x18, 0x87, 0x73, 0x5a, 0x0a, 0x1e, 0xd4, 0x3f, 0xa7, 0x42, 0x70, 0xb8, 0x16, 0x41, 0xa8, 0x83, + 0x39, 0xaa, 0x9b, 0xa3, 0xd0, 0x41, 0x04, 0x91, 0x42, 0x1d, 0x5c, 0xc2, 0x35, 0x77, 0x4d, 0x0f, + 0x9b, 0xbc, 0xe1, 0xee, 0x1a, 0xe3, 0xb7, 0x70, 0x74, 0xf4, 0xbb, 0xb8, 0x38, 0x76, 0x74, 0x94, + 0xe4, 0x8b, 0x48, 0x2f, 0x31, 0x6e, 0xc7, 0xef, 0x79, 0xee, 0x1d, 0x1e, 0xdc, 0x97, 0x05, 0x44, + 0xa0, 0x25, 0x13, 0x10, 0xcf, 0x01, 0x04, 0xcb, 0x47, 0x2c, 0xe3, 0x9a, 0x27, 0x26, 0xc8, 0x34, + 0x58, 0x20, 0xa4, 0x11, 0x82, 0x46, 0x08, 0xf2, 0xd1, 0xc9, 0x51, 0x0c, 0x31, 0x38, 0xcc, 0x36, + 0xaf, 0xda, 0x3c, 0xfd, 0x44, 0xb8, 0xfb, 0xe0, 0xbe, 0x92, 0x4b, 0x7c, 0x2c, 0x33, 0x88, 0x16, + 0x26, 0x5c, 0xa5, 0x56, 0x2d, 0xc3, 0x55, 0x3a, 0x83, 0x54, 0x48, 0xe1, 0xa3, 0x01, 0x1a, 0xf6, + 0x26, 0x87, 0x35, 0x9c, 0x6e, 0xd8, 0xb4, 0x41, 0xe4, 0x1c, 0xef, 0xbb, 0x39, 0x54, 0x42, 0xa6, + 0x56, 0xcd, 0x95, 0xd4, 0xfe, 0xd6, 0x00, 0x0d, 0x77, 0x26, 0x7b, 0x6e, 0xbf, 0x6d, 0x67, 0x72, + 0x86, 0x77, 0x13, 0x5e, 0x84, 0x39, 0x5f, 0x2a, 0xc1, 0x2d, 0x68, 0xe3, 0x6f, 0xbb, 0xbb, 0xbd, + 0x84, 0x17, 0x8f, 0xed, 0x48, 0x2e, 0x30, 0x59, 0x28, 0x63, 0x41, 0xab, 0x88, 0x2f, 0x43, 0x99, + 0x5a, 0xad, 0xa4, 0xf1, 0x3b, 0x4e, 0x3d, 0xf8, 0x27, 0xe3, 0x1a, 0x5c, 0x77, 0xde, 0x3f, 0xfa, + 0xde, 0xcd, 0xdd, 0x57, 0x49, 0xd1, 0xba, 0xa4, 0xe8, 0xa7, 0xa4, 0xe8, 0xad, 0xa2, 0xde, 0xba, + 0xa2, 0xde, 0x77, 0x45, 0xbd, 0xa7, 0x51, 0xac, 0xec, 0x62, 0x35, 0x0b, 0x22, 0x48, 0xd8, 0xb8, + 0x8e, 0x72, 0x2f, 0xed, 0x0b, 0xe8, 0x67, 0xf6, 0x17, 0xb1, 0x68, 0x33, 0xda, 0xd7, 0x4c, 0x9a, + 0x59, 0xd7, 0x95, 0xb9, 0xfa, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x52, 0x3e, 0xbb, 0xe0, 0x66, 0x01, + 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -99,6 +147,28 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.HistoricalEntries != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.HistoricalEntries)) + i-- + dAtA[i] = 0x20 + } + if m.MaxValidators != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxValidators)) + i-- + dAtA[i] = 0x18 + } + if len(m.EpochIdentifier) > 0 { + i -= len(m.EpochIdentifier) + copy(dAtA[i:], m.EpochIdentifier) + i = encodeVarintParams(dAtA, i, uint64(len(m.EpochIdentifier))) + i-- + dAtA[i] = 0x12 + } + if m.EpochsUntilUnbonded != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.EpochsUntilUnbonded)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -119,6 +189,19 @@ func (m *Params) Size() (n int) { } var l int _ = l + if m.EpochsUntilUnbonded != 0 { + n += 1 + sovParams(uint64(m.EpochsUntilUnbonded)) + } + l = len(m.EpochIdentifier) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if m.MaxValidators != 0 { + n += 1 + sovParams(uint64(m.MaxValidators)) + } + if m.HistoricalEntries != 0 { + n += 1 + sovParams(uint64(m.HistoricalEntries)) + } return n } @@ -157,6 +240,95 @@ func (m *Params) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochsUntilUnbonded", wireType) + } + m.EpochsUntilUnbonded = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EpochsUntilUnbonded |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochIdentifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EpochIdentifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxValidators", wireType) + } + m.MaxValidators = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxValidators |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HistoricalEntries", wireType) + } + m.HistoricalEntries = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HistoricalEntries |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/dogfood/types/utils.go b/x/dogfood/types/utils.go new file mode 100644 index 000000000..079a21dec --- /dev/null +++ b/x/dogfood/types/utils.go @@ -0,0 +1,31 @@ +package types + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" +) + +// TMCryptoPublicKeyToConsAddr converts a TM public key to an SDK public key +// and returns the associated consensus address. +func TMCryptoPublicKeyToConsAddr(k tmprotocrypto.PublicKey) (sdk.ConsAddress, error) { + sdkK, err := cryptocodec.FromTmProtoPublicKey(k) + if err != nil { + return nil, err + } + return sdk.GetConsAddress(sdkK), nil +} + +// RemoveFromBytesList removes an address from a list of addresses +// or a byte slice from a list of byte slices. +func RemoveFromBytesList(list [][]byte, addr []byte) [][]byte { + for i, a := range list { + if bytes.Equal(a, addr) { + return append(list[:i], list[i+1:]...) + } + } + panic("address not found in list") +} diff --git a/x/dogfood/types/validator.go b/x/dogfood/types/validator.go new file mode 100644 index 000000000..dafb75065 --- /dev/null +++ b/x/dogfood/types/validator.go @@ -0,0 +1,45 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewExocoreValidator creates an ExocoreValidator with the specified (consensus) address, vote +// power and consensus public key. If the public key is malformed, it returns an error. +func NewExocoreValidator( + address []byte, power int64, pubKey cryptotypes.PubKey, +) (ExocoreValidator, error) { + pkAny, err := codectypes.NewAnyWithValue(pubKey) + if err != nil { + return ExocoreValidator{}, errorsmod.Wrap(err, "failed to pack public key") + } + return ExocoreValidator{ + Address: address, + Power: power, + Pubkey: pkAny, + }, nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces. +// It is required to ensure that ConsPubKey below works. +func (ecv ExocoreValidator) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var pk cryptotypes.PubKey + return unpacker.UnpackAny(ecv.Pubkey, &pk) +} + +// ConsPubKey returns the validator PubKey as a cryptotypes.PubKey. +func (ecv ExocoreValidator) ConsPubKey() (cryptotypes.PubKey, error) { + pk, ok := ecv.Pubkey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidType, + "expecting cryptotypes.PubKey, got %T", + pk, + ) + } + + return pk, nil +}