Skip to content

Commit

Permalink
feat(sequencer): Allow a sequencer to increase their bond (#1015)
Browse files Browse the repository at this point in the history
  • Loading branch information
spoo-bar authored Aug 1, 2024
1 parent 48cf96e commit 7d27b75
Show file tree
Hide file tree
Showing 13 changed files with 1,188 additions and 40 deletions.
23 changes: 23 additions & 0 deletions proto/dymensionxyz/dymension/sequencer/events.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
syntax = "proto3";

package dymensionxyz.dymension.sequencer;

import "dymensionxyz/dymension/sequencer/description.proto";
import "google/protobuf/any.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "google/protobuf/timestamp.proto";
import "cosmos/msg/v1/msg.proto";

option go_package = "github.com/dymensionxyz/dymension/v3/x/sequencer/types";

// EventIncreasedBond is an event emitted when a sequencer's bond is increased.
message EventIncreasedBond {
// sequencer is the bech32-encoded address of the sequencer which increased its bond
string sequencer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// added_amount is the amount of coins added to the sequencer's bond
cosmos.base.v1beta1.Coin added_amount = 2 [(gogoproto.nullable) = false];
// bond is the new active bond amount of the sequencer
repeated cosmos.base.v1beta1.Coin bond = 3 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
}
18 changes: 17 additions & 1 deletion proto/dymensionxyz/dymension/sequencer/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ service Msg {

// Unbond defines a method for removing coins from sequencer's bond
rpc Unbond (MsgUnbond) returns (MsgUnbondResponse);

// IncreaseBond defines a method for increasing a sequencer's bond amount
rpc IncreaseBond (MsgIncreaseBond) returns (MsgIncreaseBondResponse);
}
// MsgCreateSequencer defines a SDK message for creating a new sequencer.
message MsgCreateSequencer {
Expand Down Expand Up @@ -53,4 +56,17 @@ message MsgUnbond {
// MsgUnbondResponse defines the Msg/Unbond response type.
message MsgUnbondResponse {
google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
}
}

// MsgIncreaseBond defines a SDK message for increasing the bond amount of a sequencer.
message MsgIncreaseBond {
option (cosmos.msg.v1.signer) = "creator";
// creator is the bech32-encoded address of the sequencer account which is the account that the message was sent from.
string creator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// add_amount is the amount of coins to be added to the sequencer's bond.
cosmos.base.v1beta1.Coin add_amount = 2 [(gogoproto.nullable) = false];
}

// MsgIncreaseBondResponse defines the Msg/IncreaseBond response type.
message MsgIncreaseBondResponse {}
33 changes: 32 additions & 1 deletion x/sequencer/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func GetTxCmd() *cobra.Command {

cmd.AddCommand(CmdCreateSequencer())
cmd.AddCommand(CmdUnbond())
// this line is used by starport scaffolding # 1
cmd.AddCommand(CmdIncreaseBond())

return cmd
}
Expand Down Expand Up @@ -114,3 +114,34 @@ func CmdUnbond() *cobra.Command {

return cmd
}

func CmdIncreaseBond() *cobra.Command {
cmd := &cobra.Command{
Use: "increase-bond [amount]",
Short: "Increase the bond of a sequencer",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
amount := args[0]
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

amountCoin, err := sdk.ParseCoinNormalized(amount)
if err != nil {
return err
}

msg := types.NewMsgIncreaseBond(
clientCtx.GetFromAddress().String(),
amountCoin,
)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
3 changes: 3 additions & 0 deletions x/sequencer/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgUnbond:
res, err := msgServer.Unbond(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgIncreaseBond:
res, err := msgServer.IncreaseBond(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
return nil, errorsmod.Wrap(types.ErrUnknownRequest, errMsg)
Expand Down
59 changes: 59 additions & 0 deletions x/sequencer/keeper/msg_server_increase_bond.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/dymensionxyz/dymension/v3/x/sequencer/types"
)

// IncreaseBond implements types.MsgServer.
func (k msgServer) IncreaseBond(goCtx context.Context, msg *types.MsgIncreaseBond) (*types.MsgIncreaseBondResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

sequencer, err := k.bondUpdateAllowed(ctx, msg)
if err != nil {
return nil, err
}

// transfer the bond from the sequencer to the module account
seqAcc := sdk.MustAccAddressFromBech32(msg.Creator)
err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, seqAcc, types.ModuleName, sdk.NewCoins(msg.AddAmount))
if err != nil {
return nil, err
}

// update the sequencers bond amount in state
sequencer.Tokens = sequencer.Tokens.Add(msg.AddAmount)
k.UpdateSequencer(ctx, sequencer, sequencer.Status)

// emit a typed event which includes the added amount and the active bond amount
err = ctx.EventManager().EmitTypedEvent(
&types.EventIncreasedBond{
Sequencer: msg.Creator,
Bond: sequencer.Tokens,
AddedAmount: msg.AddAmount,
},
)

return &types.MsgIncreaseBondResponse{}, err
}

func (k msgServer) bondUpdateAllowed(ctx sdk.Context, msg *types.MsgIncreaseBond) (types.Sequencer, error) {
// check if the sequencer already exists
sequencer, found := k.GetSequencer(ctx, msg.Creator)
if !found {
return types.Sequencer{}, types.ErrUnknownSequencer
}

// check if the sequencer is bonded
if !sequencer.IsBonded() {
return types.Sequencer{}, types.ErrInvalidSequencerStatus
}

// check if sequencer is currently jailed
if sequencer.Jailed {
return types.Sequencer{}, types.ErrSequencerJailed
}
return sequencer, nil
}
95 changes: 95 additions & 0 deletions x/sequencer/keeper/msg_server_increase_bond_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package keeper_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
bankutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/dymensionxyz/dymension/v3/testutil/sample"
"github.com/dymensionxyz/dymension/v3/x/sequencer/types"
)

func (suite *SequencerTestSuite) TestIncreaseBond() {
suite.SetupTest()
rollappId := suite.CreateDefaultRollapp()
// setup a default sequencer
defaultSequencerAddress := suite.CreateDefaultSequencer(suite.Ctx, rollappId)
// setup an unbonded sequencer
unbondedSequencerAddress := suite.CreateDefaultSequencer(suite.Ctx, rollappId)
unbondedSequencer, _ := suite.App.SequencerKeeper.GetSequencer(suite.Ctx, unbondedSequencerAddress)
unbondedSequencer.Status = types.Unbonded
suite.App.SequencerKeeper.UpdateSequencer(suite.Ctx, unbondedSequencer, unbondedSequencer.Status)
// setup a jailed sequencer
jailedSequencerAddress := suite.CreateDefaultSequencer(suite.Ctx, rollappId)
jailedSequencer, _ := suite.App.SequencerKeeper.GetSequencer(suite.Ctx, jailedSequencerAddress)
jailedSequencer.Jailed = true
suite.App.SequencerKeeper.UpdateSequencer(suite.Ctx, jailedSequencer, jailedSequencer.Status)
// fund all the sequencers which have been setup
bondAmount := sdk.NewInt64Coin(types.DefaultParams().MinBond.Denom, 100)
err := bankutil.FundAccount(suite.App.BankKeeper, suite.Ctx, sdk.MustAccAddressFromBech32(defaultSequencerAddress), sdk.NewCoins(bondAmount))
suite.Require().NoError(err)
err = bankutil.FundAccount(suite.App.BankKeeper, suite.Ctx, sdk.MustAccAddressFromBech32(unbondedSequencerAddress), sdk.NewCoins(bondAmount))
suite.Require().NoError(err)
err = bankutil.FundAccount(suite.App.BankKeeper, suite.Ctx, sdk.MustAccAddressFromBech32(jailedSequencerAddress), sdk.NewCoins(bondAmount))
suite.Require().NoError(err)

testCase := []struct {
name string
msg types.MsgIncreaseBond
expectedErr error
}{
{
name: "valid",
msg: types.MsgIncreaseBond{
Creator: defaultSequencerAddress,
AddAmount: bondAmount,
},
expectedErr: nil,
},
{
name: "invalid sequencer",
msg: types.MsgIncreaseBond{
Creator: sample.AccAddress(), // a random address which is not a registered sequencer
AddAmount: bondAmount,
},
expectedErr: types.ErrUnknownSequencer,
},
{
name: "invalid sequencer status",
msg: types.MsgIncreaseBond{
Creator: unbondedSequencerAddress,
AddAmount: bondAmount,
},
expectedErr: types.ErrInvalidSequencerStatus,
},
{
name: "jailed sequencer",
msg: types.MsgIncreaseBond{
Creator: jailedSequencerAddress,
AddAmount: bondAmount,
},
expectedErr: types.ErrSequencerJailed,
},
{
name: "sequencer doesn't have enough balance",
msg: types.MsgIncreaseBond{
Creator: defaultSequencerAddress,
AddAmount: sdk.NewInt64Coin(types.DefaultParams().MinBond.Denom, 99999999), // very high amount which sequencer doesn't have
},
expectedErr: sdkerrors.ErrInsufficientFunds,
},
}

for _, tc := range testCase {
suite.Run(tc.name, func() {
_, err := suite.msgServer.IncreaseBond(suite.Ctx, &tc.msg)
if tc.expectedErr != nil {
suite.Require().ErrorIs(err, tc.expectedErr)
} else {
suite.Require().NoError(err)
expectedBond := types.DefaultParams().MinBond.Add(bondAmount)
seq, _ := suite.App.SequencerKeeper.GetSequencer(suite.Ctx, defaultSequencerAddress)
suite.Require().Equal(expectedBond, seq.Tokens[0])
}
})
}
}
4 changes: 2 additions & 2 deletions x/sequencer/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import (
func RegisterCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgCreateSequencer{}, "sequencer/CreateSequencer", nil)
cdc.RegisterConcrete(&MsgUnbond{}, "sequencer/Unbond", nil)
// this line is used by starport scaffolding # 2
cdc.RegisterConcrete(&MsgIncreaseBond{}, "sequencer/IncreaseBond", nil)
}

func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgCreateSequencer{},
&MsgUnbond{},
&MsgIncreaseBond{},
)
// this line is used by starport scaffolding # 3

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}
Expand Down
1 change: 1 addition & 0 deletions x/sequencer/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ var (
ErrInvalidType = errorsmod.Register(ModuleName, 1016, "invalid type")
ErrUnknownRequest = errorsmod.Register(ModuleName, 1017, "unknown request")
ErrInvalidRequest = errorsmod.Register(ModuleName, 1018, "invalid request")
ErrSequencerJailed = errorsmod.Register(ModuleName, 1019, "sequencer is jailed")
)
3 changes: 3 additions & 0 deletions x/sequencer/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ const (

// EventTypeSlashed is emitted when a sequencer is slashed
EventTypeSlashed = "slashed"

// EventTypeBondIncreased is emitted when a sequencer's bond is increased
EventTypeBondIncreased = "bond_increased"
)
Loading

0 comments on commit 7d27b75

Please sign in to comment.