diff --git a/.gitignore b/.gitignore index 301a95c1b..2fc209488 100644 --- a/.gitignore +++ b/.gitignore @@ -208,3 +208,6 @@ $RECYCLE.BIN/ tools-stamp /tests/localbabylon/.babylond/* docs/diagrams/plantuml.jar +.testnets/ +mytestnet/ +output/ \ No newline at end of file diff --git a/app/app.go b/app/app.go index 9f22f006b..b0b76f0c6 100644 --- a/app/app.go +++ b/app/app.go @@ -129,6 +129,7 @@ var ( evidence.AppModuleBasic{}, authzmodule.AppModuleBasic{}, vesting.AppModuleBasic{}, + epoching.AppModuleBasic{}, btclightclient.AppModuleBasic{}, btccheckpoint.AppModuleBasic{}, checkpointing.AppModuleBasic{}, @@ -483,7 +484,7 @@ func NewBabylonApp( } anteHandler := sdk.ChainAnteDecorators( NewWrappedAnteHandler(authAnteHandler), - epochingkeeper.NewDropValidatorMsgDecorator(), + epochingkeeper.NewDropValidatorMsgDecorator(app.EpochingKeeper), ) app.SetAnteHandler(anteHandler) diff --git a/proto/babylon/epoching/v1/params.proto b/proto/babylon/epoching/v1/params.proto index 5301ee386..9da17151d 100644 --- a/proto/babylon/epoching/v1/params.proto +++ b/proto/babylon/epoching/v1/params.proto @@ -7,6 +7,8 @@ option go_package = "github.com/babylonchain/babylon/x/epoching/types"; // Params defines the parameters for the module. message Params { + option (gogoproto.equal) = true; + // epoch_interval is the number of consecutive blocks to form an epoch uint64 epoch_interval = 1 [ (gogoproto.moretags) = "yaml:\"epoch_interval\"" ]; } diff --git a/x/epoching/genesis.go b/x/epoching/genesis.go index 78bcb7636..81bed3548 100644 --- a/x/epoching/genesis.go +++ b/x/epoching/genesis.go @@ -1,16 +1,20 @@ package epoching import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/babylonchain/babylon/x/epoching/keeper" "github.com/babylonchain/babylon/x/epoching/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) // InitGenesis initializes the capability module's state from a provided genesis // state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { - // this line is used by starport scaffolding # genesis/module/init + // set params for this module k.SetParams(ctx, genState.Params) + // init epoch number + if err := k.SetEpochNumber(ctx, sdk.NewUint(0)); err != nil { + panic(err) + } } // ExportGenesis returns the capability module's exported genesis. @@ -18,7 +22,5 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() genesis.Params = k.GetParams(ctx) - // this line is used by starport scaffolding # genesis/module/export - - return genesis + return genesis } diff --git a/x/epoching/genesis_test.go b/x/epoching/genesis_test.go index 5983193bc..ddcc3fb1b 100644 --- a/x/epoching/genesis_test.go +++ b/x/epoching/genesis_test.go @@ -1 +1,35 @@ package epoching_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/epoching" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + simapp "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/x/epoching/types" +) + +func TestExportGenesis(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.EpochingKeeper.SetParams(ctx, types.DefaultParams()) + genesisState := epoching.ExportGenesis(ctx, app.EpochingKeeper) + require.Equal(t, genesisState.Params, types.DefaultParams()) +} + +func TestInitGenesis(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + genesisState := types.GenesisState{ + Params: types.Params{ + EpochInterval: 100, + }, + } + + epoching.InitGenesis(ctx, app.EpochingKeeper, genesisState) + require.Equal(t, app.EpochingKeeper.GetParams(ctx).EpochInterval, uint64(100)) +} diff --git a/x/epoching/keeper/drop_validator_msg_decorator.go b/x/epoching/keeper/drop_validator_msg_decorator.go index 0db346451..6022c983c 100644 --- a/x/epoching/keeper/drop_validator_msg_decorator.go +++ b/x/epoching/keeper/drop_validator_msg_decorator.go @@ -7,11 +7,15 @@ import ( ) // DropValidatorMsgDecorator defines an AnteHandler decorator that rejects all messages that might change the validator set. -type DropValidatorMsgDecorator struct{} +type DropValidatorMsgDecorator struct { + ek Keeper +} // NewDropValidatorMsgDecorator creates a new DropValidatorMsgDecorator -func NewDropValidatorMsgDecorator() *DropValidatorMsgDecorator { - return &DropValidatorMsgDecorator{} +func NewDropValidatorMsgDecorator(ek Keeper) *DropValidatorMsgDecorator { + return &DropValidatorMsgDecorator{ + ek: ek, + } } // AnteHandle performs an AnteHandler check that rejects all non-wrapped validator-related messages. @@ -22,7 +26,12 @@ func NewDropValidatorMsgDecorator() *DropValidatorMsgDecorator { // - MsgBeginRedelegate // TODO: after we bump to Cosmos SDK v0.46, add MsgCancelUnbondingDelegation func (qmd DropValidatorMsgDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + // skip if at genesis block, as genesis state contains txs that bootstrap the initial validator set + if ctx.BlockHeight() == 0 { + return next(ctx, tx, simulate) + } for _, msg := range tx.GetMsgs() { + // if validator-related message after genesis, reject msg if qmd.IsValidatorRelatedMsg(msg) { return ctx, epochingtypes.ErrInvalidMsgType } diff --git a/x/epoching/keeper/drop_validator_msg_decorator_test.go b/x/epoching/keeper/drop_validator_msg_decorator_test.go index 7d9cd331f..e6c5ffa17 100644 --- a/x/epoching/keeper/drop_validator_msg_decorator_test.go +++ b/x/epoching/keeper/drop_validator_msg_decorator_test.go @@ -23,7 +23,7 @@ func TestDropValidatorMsgDecorator(t *testing.T) { {&stakingtypes.MsgEditValidator{}, false}, } - decorator := NewDropValidatorMsgDecorator() + decorator := NewDropValidatorMsgDecorator(Keeper{}) for _, tc := range testCases { res := decorator.IsValidatorRelatedMsg(tc.msg) diff --git a/x/epoching/keeper/keeper.go b/x/epoching/keeper/keeper.go index 88777d60f..15e143a2f 100644 --- a/x/epoching/keeper/keeper.go +++ b/x/epoching/keeper/keeper.go @@ -10,10 +10,6 @@ import ( "github.com/tendermint/tendermint/libs/log" ) -const ( - DefaultEpochNumber = 0 -) - type ( Keeper struct { cdc codec.BinaryCodec @@ -68,7 +64,7 @@ func (k Keeper) GetEpochNumber(ctx sdk.Context) (sdk.Uint, error) { bz := store.Get(types.EpochNumberKey) if bz == nil { - return sdk.NewUint(uint64(DefaultEpochNumber)), nil + return sdk.NewUint(0), types.ErrUnknownEpochNumber } var epochNumber sdk.Uint err := epochNumber.Unmarshal(bz) @@ -76,8 +72,8 @@ func (k Keeper) GetEpochNumber(ctx sdk.Context) (sdk.Uint, error) { return epochNumber, err } -// setEpochNumber sets epoch number -func (k Keeper) setEpochNumber(ctx sdk.Context, epochNumber sdk.Uint) error { +// SetEpochNumber sets epoch number +func (k Keeper) SetEpochNumber(ctx sdk.Context, epochNumber sdk.Uint) error { store := ctx.KVStore(k.storeKey) epochNumberBytes, err := epochNumber.Marshal() @@ -97,7 +93,7 @@ func (k Keeper) IncEpochNumber(ctx sdk.Context) error { return err } incrementedEpochNumber := epochNumber.AddUint64(1) - return k.setEpochNumber(ctx, incrementedEpochNumber) + return k.SetEpochNumber(ctx, incrementedEpochNumber) } // GetEpochBoundary gets the epoch boundary, i.e., the height of the block that ends this epoch @@ -106,9 +102,15 @@ func (k Keeper) GetEpochBoundary(ctx sdk.Context) (sdk.Uint, error) { if err != nil { return sdk.NewUint(0), err } + // epoch number is 0 at the 0-th block, i.e., genesis + if epochNumber.IsZero() { + return sdk.NewUint(0), nil + } epochInterval := sdk.NewUint(k.GetParams(ctx).EpochInterval) - // example: in epoch 0, epoch interval is 5 blocks, boundary will be (0+1)*5=5 - return epochNumber.AddUint64(1).Mul(epochInterval), nil + // example: in epoch 1, epoch interval is 5 blocks, boundary will be 1*5=5 + // 0 | 1 2 3 4 5 | 6 7 8 9 10 | + // 0 | 1 | 2 | + return epochNumber.Mul(epochInterval), nil } // GetQueueLength fetches the number of queued messages diff --git a/x/epoching/types/errors.go b/x/epoching/types/errors.go index bea0a5c4b..e24f61eb0 100644 --- a/x/epoching/types/errors.go +++ b/x/epoching/types/errors.go @@ -8,5 +8,6 @@ import ( // x/epoching module sentinel errors var ( - ErrInvalidMsgType = sdkerrors.Register(ModuleName, 1, "invalid message type in {MsgCreateValidator, MsgDelegate, MsgUndelegate, MsgBeginRedelegate} messages. use wrapped versions instead") + ErrInvalidMsgType = sdkerrors.Register(ModuleName, 1, "invalid message type in {MsgCreateValidator, MsgDelegate, MsgUndelegate, MsgBeginRedelegate} messages. use wrapped versions instead") + ErrUnknownEpochNumber = sdkerrors.Register(ModuleName, 2, "the epoch number is not known in DB") ) diff --git a/x/epoching/types/genesis.go b/x/epoching/types/genesis.go index aa82115f8..532bdecc7 100644 --- a/x/epoching/types/genesis.go +++ b/x/epoching/types/genesis.go @@ -1,8 +1,6 @@ package types -import ( // this line is used by starport scaffolding # genesis/types/import -) // DefaultIndex is the default capability global index const DefaultIndex uint64 = 1 @@ -10,15 +8,15 @@ const DefaultIndex uint64 = 1 // DefaultGenesis returns the default Capability genesis state func DefaultGenesis() *GenesisState { return &GenesisState{ - // this line is used by starport scaffolding # genesis/types/default - Params: DefaultParams(), + // this line is used by starport scaffolding # genesis/types/default + Params: DefaultParams(), } } // Validate performs basic genesis state validation returning an error upon any // failure. func (gs GenesisState) Validate() error { - // this line is used by starport scaffolding # genesis/types/validate + // this line is used by starport scaffolding # genesis/types/validate return gs.Params.Validate() } diff --git a/x/epoching/types/genesis_test.go b/x/epoching/types/genesis_test.go index bc4958ff1..58e5908b6 100644 --- a/x/epoching/types/genesis_test.go +++ b/x/epoching/types/genesis_test.go @@ -3,38 +3,56 @@ package types_test import ( "testing" - "github.com/stretchr/testify/require" + keepertest "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/testutil/nullify" + "github.com/babylonchain/babylon/x/epoching" + "github.com/babylonchain/babylon/x/epoching/types" + "github.com/stretchr/testify/require" ) +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + } + + k, ctx := keepertest.EpochingKeeper(t) + epoching.InitGenesis(ctx, *k, genesisState) + got := epoching.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + nullify.Fill(&genesisState) + nullify.Fill(got) +} + func TestGenesisState_Validate(t *testing.T) { - for _, tc := range []struct { - desc string - genState *types.GenesisState - valid bool - } { - { - desc: "default is valid", - genState: types.DefaultGenesis(), - valid: true, - }, - { - desc: "valid genesis state", - genState: &types.GenesisState{ - - // this line is used by starport scaffolding # types/genesis/validField - }, - valid: true, - }, - // this line is used by starport scaffolding # types/genesis/testcase - } { - t.Run(tc.desc, func(t *testing.T) { - err := tc.genState.Validate() - if tc.valid { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} \ No newline at end of file + for _, tc := range []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + { + desc: "valid genesis state", + genState: &types.GenesisState{ + Params: types.Params{ + EpochInterval: 100, + }, + }, + valid: true, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/epoching/types/params.go b/x/epoching/types/params.go index 4f3215e35..4c39fdb7b 100644 --- a/x/epoching/types/params.go +++ b/x/epoching/types/params.go @@ -1,9 +1,19 @@ package types import ( + fmt "fmt" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) +const ( + DefaultEpochInterval uint64 = 10 +) + +var ( + KeyEpochInterval = []byte("EpochInterval") +) + var _ paramtypes.ParamSet = (*Params)(nil) // ParamKeyTable the param key table for launch module @@ -12,21 +22,42 @@ func ParamKeyTable() paramtypes.KeyTable { } // NewParams creates a new Params instance -func NewParams() Params { - return Params{} -} - -// DefaultParams returns a default set of parameters -func DefaultParams() Params { - return NewParams() +func NewParams(epochInterval uint64) Params { + return Params{ + EpochInterval: epochInterval, + } } // ParamSetPairs get the params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { - return paramtypes.ParamSetPairs{} + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyEpochInterval, &p.EpochInterval, validateEpochInterval), + } +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return NewParams(DefaultEpochInterval) } // Validate validates the set of params func (p Params) Validate() error { + if err := validateEpochInterval(p.EpochInterval); err != nil { + return err + } + + return nil +} + +func validateEpochInterval(i interface{}) error { + v, ok := i.(uint64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v <= 0 { + return fmt.Errorf("epoch interval must be positive: %d", v) + } + return nil } diff --git a/x/epoching/types/params.pb.go b/x/epoching/types/params.pb.go index 7371d14b1..3fbf60413 100644 --- a/x/epoching/types/params.pb.go +++ b/x/epoching/types/params.pb.go @@ -76,22 +76,46 @@ func init() { func init() { proto.RegisterFile("babylon/epoching/v1/params.proto", fileDescriptor_c9e38cfe55335900) } var fileDescriptor_c9e38cfe55335900 = []byte{ - // 195 bytes of a gzipped FileDescriptorProto + // 201 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2d, 0xc8, 0x4f, 0xce, 0xc8, 0xcc, 0x4b, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0xaa, 0xd0, 0x83, 0xa9, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, - 0x58, 0x10, 0xa5, 0x4a, 0x5e, 0x5c, 0x6c, 0x01, 0x60, 0xad, 0x42, 0x0e, 0x5c, 0x7c, 0x60, 0xe5, + 0x58, 0x10, 0xa5, 0x4a, 0x01, 0x5c, 0x6c, 0x01, 0x60, 0xad, 0x42, 0x0e, 0x5c, 0x7c, 0x60, 0xe5, 0xf1, 0x99, 0x79, 0x25, 0xa9, 0x45, 0x65, 0x89, 0x39, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, 0x4e, 0x92, 0x9f, 0xee, 0xc9, 0x8b, 0x56, 0x26, 0xe6, 0xe6, 0x58, 0x29, 0xa1, 0xca, 0x2b, 0x05, 0xf1, - 0x82, 0x05, 0x3c, 0xa1, 0x7c, 0x27, 0xaf, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, - 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, - 0x88, 0x32, 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0xba, 0x2d, - 0x39, 0x23, 0x31, 0x33, 0x0f, 0xc6, 0xd1, 0xaf, 0x40, 0x78, 0xa6, 0xa4, 0xb2, 0x20, 0xb5, 0x38, - 0x89, 0x0d, 0xec, 0x3c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x1c, 0x2b, 0x74, 0xed, - 0x00, 0x00, 0x00, + 0x82, 0x05, 0x3c, 0xa1, 0x7c, 0x2b, 0x96, 0x17, 0x0b, 0xe4, 0x19, 0x9d, 0xbc, 0x4e, 0x3c, 0x92, + 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, + 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x20, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, + 0x39, 0x3f, 0x57, 0x1f, 0xea, 0xc2, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x18, 0x47, 0xbf, 0x02, 0xe1, + 0xa5, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, 0x23, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xa6, 0x7d, 0x61, 0x54, 0xf3, 0x00, 0x00, 0x00, } +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.EpochInterval != that1.EpochInterval { + return false + } + return true +} func (m *Params) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) diff --git a/x/epoching/types/params_test.go b/x/epoching/types/params_test.go new file mode 100644 index 000000000..83424dd41 --- /dev/null +++ b/x/epoching/types/params_test.go @@ -0,0 +1,21 @@ +package types_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/epoching/types" + "github.com/stretchr/testify/require" +) + +func TestParamsEqual(t *testing.T) { + p1 := types.DefaultParams() + p2 := types.DefaultParams() + + ok := p1.Equal(p2) + require.True(t, ok) + + p2.EpochInterval = 100 + + ok = p1.Equal(p2) + require.False(t, ok) +}