Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tx-boundary module #224

Merged
merged 14 commits into from
Aug 28, 2023
3 changes: 3 additions & 0 deletions app/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
ante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
tfmwKeeper "github.com/notional-labs/centauri/v4/x/transfermiddleware/keeper"
txBoundaryKeeper "github.com/notional-labs/centauri/v4/x/tx-boundary/keeper"
)

// Link to default ante handler used by cosmos sdk:
Expand All @@ -21,6 +22,7 @@ func NewAnteHandler(
signModeHandler signing.SignModeHandler,
channelKeeper *ibckeeper.Keeper,
tfmwKeeper tfmwKeeper.Keeper,
txBoundaryKeeper txBoundaryKeeper.Keeper,
codec codec.BinaryCodec,
) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
Expand All @@ -30,6 +32,7 @@ func NewAnteHandler(
ante.NewValidateMemoDecorator(ak),
ante.NewConsumeGasForTxSizeDecorator(ak),
NewIBCPermissionDecorator(codec, tfmwKeeper),
NewStakingPermissionDecorator(codec, txBoundaryKeeper),
ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators
ante.NewValidateSigCountDecorator(ak),
ante.NewSigGasConsumeDecorator(ak, sigGasConsumer),
Expand Down
84 changes: 84 additions & 0 deletions app/ante/custom_ante.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package ante
expertdicer marked this conversation as resolved.
Show resolved Hide resolved

expertdicer marked this conversation as resolved.
Show resolved Hide resolved
import (
"fmt"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"

stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
txBoundaryKeeper "github.com/notional-labs/centauri/v4/x/tx-boundary/keeper"
)

type StakingPermissionDecorator struct {
cdc codec.BinaryCodec
txBoundary txBoundaryKeeper.Keeper
}

func NewStakingPermissionDecorator(cdc codec.BinaryCodec, keeper txBoundaryKeeper.Keeper) StakingPermissionDecorator {
return StakingPermissionDecorator{
cdc: cdc,
txBoundary: keeper,
}
}

func (g StakingPermissionDecorator) AnteHandle(
ctx sdk.Context, tx sdk.Tx,
simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
// run checks only on CheckTx or simulate
if simulate {
return next(ctx, tx, simulate)
}

msgs := tx.GetMsgs()
if err = g.ValidateStakingMsg(ctx, msgs); err != nil {
return ctx, err
}

return next(ctx, tx, simulate)
}

// ValidateStakingMsg validate
func (g StakingPermissionDecorator) ValidateStakingMsg(ctx sdk.Context, msgs []sdk.Msg) error {
for _, m := range msgs {
switch msg := m.(type) {
case *stakingtypes.MsgDelegate:
if err := g.validDelegateMsg(ctx, msg); err != nil {
return err
}
case *stakingtypes.MsgBeginRedelegate:
if err := g.validRedelegateMsg(ctx, msg); err != nil {
return err
}
default:
return nil
}
}
return nil
}

func (g StakingPermissionDecorator) validDelegateMsg(ctx sdk.Context, msg *stakingtypes.MsgDelegate) error {
boundary := g.txBoundary.GetDelegateBoundary(ctx)
g.txBoundary.UpdateLimitPerAddr(ctx, sdk.AccAddress(msg.DelegatorAddress))
if boundary.TxLimit == 0 {
return nil
} else if g.txBoundary.GetLimitPerAddr(ctx, sdk.AccAddress(msg.DelegatorAddress)).DelegateCount > boundary.TxLimit {
return fmt.Errorf("delegate tx denied, excess tx limit")
}
g.txBoundary.IncrementDelegateCount(ctx, sdk.AccAddress(msg.DelegatorAddress))

return nil
}

func (g StakingPermissionDecorator) validRedelegateMsg(ctx sdk.Context, msg *stakingtypes.MsgBeginRedelegate) error {
expertdicer marked this conversation as resolved.
Show resolved Hide resolved
boundary := g.txBoundary.GetRedelegateBoundary(ctx)
g.txBoundary.UpdateLimitPerAddr(ctx, sdk.AccAddress(msg.DelegatorAddress))
if boundary.TxLimit == 0 {
return nil
} else if g.txBoundary.GetLimitPerAddr(ctx, sdk.AccAddress(msg.DelegatorAddress)).ReledegateCount > boundary.TxLimit {
return fmt.Errorf("redelegate tx denied, excess tx limit")
}
g.txBoundary.IncrementRedelegateCount(ctx, sdk.AccAddress(msg.DelegatorAddress))
return nil
}
10 changes: 10 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
transfermiddleware "github.com/notional-labs/centauri/v4/x/transfermiddleware"
transfermiddlewaretypes "github.com/notional-labs/centauri/v4/x/transfermiddleware/types"

txBoundary "github.com/notional-labs/centauri/v4/x/tx-boundary"
txBoundaryTypes "github.com/notional-labs/centauri/v4/x/tx-boundary/types"

ratelimitmodule "github.com/notional-labs/centauri/v4/x/ratelimit"
ratelimitmoduletypes "github.com/notional-labs/centauri/v4/x/ratelimit/types"

Expand Down Expand Up @@ -208,6 +211,7 @@
ica.AppModuleBasic{},
ibc_hooks.AppModuleBasic{},
transfermiddleware.AppModuleBasic{},
txBoundary.AppModuleBasic{},
ratelimitmodule.AppModuleBasic{},
consensus.AppModuleBasic{},
alliancemodule.AppModuleBasic{},
Expand Down Expand Up @@ -322,6 +326,7 @@
transferModule := transfer.NewAppModule(app.TransferKeeper)
routerModule := router.NewAppModule(app.RouterKeeper)
transfermiddlewareModule := transfermiddleware.NewAppModule(&app.TransferMiddlewareKeeper)
txBoundaryModule := txBoundary.NewAppModule(appCodec, app.TxBoundaryKeepper)
ratelimitModule := ratelimitmodule.NewAppModule(&app.RatelimitKeeper)
icqModule := icq.NewAppModule(app.ICQKeeper)
ibcHooksModule := ibc_hooks.NewAppModule()
Expand Down Expand Up @@ -364,6 +369,7 @@
wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasmtypes.ModuleName)),
routerModule,
transfermiddlewareModule,
txBoundaryModule,
icaModule,
ratelimitModule,
alliancemodule.NewAppModule(appCodec, app.AllianceKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
Expand All @@ -387,6 +393,7 @@
ibctransfertypes.ModuleName,
routertypes.ModuleName,
transfermiddlewaretypes.ModuleName,
txBoundaryTypes.ModuleName,
ratelimitmoduletypes.ModuleName,
ibchookstypes.ModuleName,
icqtypes.ModuleName,
Expand Down Expand Up @@ -426,6 +433,7 @@
ibchost.ModuleName,
routertypes.ModuleName,
transfermiddlewaretypes.ModuleName,
txBoundaryTypes.ModuleName,
ratelimitmoduletypes.ModuleName,
ibchookstypes.ModuleName,
ibctransfertypes.ModuleName,
Expand Down Expand Up @@ -462,6 +470,7 @@
icqtypes.ModuleName,
routertypes.ModuleName,
transfermiddlewaretypes.ModuleName,
txBoundaryTypes.ModuleName,
ratelimitmoduletypes.ModuleName,
ibchookstypes.ModuleName,
feegrant.ModuleName,
Expand Down Expand Up @@ -521,6 +530,7 @@
encodingConfig.TxConfig.SignModeHandler(),
app.IBCKeeper,
app.TransferMiddlewareKeeper,
app.TxBoundaryKeepper,
appCodec,
))
app.SetEndBlocker(app.EndBlocker)
Expand Down Expand Up @@ -696,7 +706,7 @@
for _, upgrade := range Upgrades {
if upgradeInfo.Name == upgrade.UpgradeName {
app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &upgrade.StoreUpgrades))
}

Check failure on line 709 in app/app.go

View workflow job for this annotation

GitHub Actions / lint

G601: Implicit memory aliasing in for loop. (gosec)
}
}

Expand Down
10 changes: 10 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ import (
transfermiddlewarekeeper "github.com/notional-labs/centauri/v4/x/transfermiddleware/keeper"
transfermiddlewaretypes "github.com/notional-labs/centauri/v4/x/transfermiddleware/types"

txBoundaryKeeper "github.com/notional-labs/centauri/v4/x/tx-boundary/keeper"
txBoundaryTypes "github.com/notional-labs/centauri/v4/x/tx-boundary/types"

ratelimitmodule "github.com/notional-labs/centauri/v4/x/ratelimit"
ratelimitmodulekeeper "github.com/notional-labs/centauri/v4/x/ratelimit/keeper"
ratelimitmoduletypes "github.com/notional-labs/centauri/v4/x/ratelimit/types"
Expand Down Expand Up @@ -145,6 +148,7 @@ type AppKeepers struct {
ConsensusParamsKeeper consensusparamkeeper.Keeper
// this line is used by starport scaffolding # stargate/app/keeperDeclaration
TransferMiddlewareKeeper transfermiddlewarekeeper.Keeper
TxBoundaryKeepper txBoundaryKeeper.Keeper
RouterKeeper *routerkeeper.Keeper
RatelimitKeeper ratelimitmodulekeeper.Keeper
AllianceKeeper alliancemodulekeeper.Keeper
Expand Down Expand Up @@ -273,6 +277,12 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
authorityAddress,
)

appKeepers.TxBoundaryKeepper = txBoundaryKeeper.NewKeeper(
appCodec,
appKeepers.keys[txBoundaryTypes.StoreKey],
authorityAddress,
)

appKeepers.TransferKeeper = ibctransferkeeper.NewKeeper(
appCodec, appKeepers.keys[ibctransfertypes.StoreKey],
appKeepers.GetSubspace(ibctransfertypes.ModuleName),
Expand Down
3 changes: 2 additions & 1 deletion app/keepers/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
ibchookstypes "github.com/notional-labs/centauri/v4/x/ibc-hooks/types"
ratelimitmoduletypes "github.com/notional-labs/centauri/v4/x/ratelimit/types"
transfermiddlewaretypes "github.com/notional-labs/centauri/v4/x/transfermiddleware/types"
txBoundaryTypes "github.com/notional-labs/centauri/v4/x/tx-boundary/types"

consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types"

Expand All @@ -48,7 +49,7 @@ func (appKeepers *AppKeepers) GenerateKeys() {
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey,
evidencetypes.StoreKey, ibctransfertypes.StoreKey, icqtypes.StoreKey, capabilitytypes.StoreKey, consensusparamtypes.StoreKey, wasm08types.StoreKey,
crisistypes.StoreKey, routertypes.StoreKey, transfermiddlewaretypes.StoreKey, group.StoreKey, minttypes.StoreKey, alliancemoduletypes.StoreKey, wasm.StoreKey, ibchookstypes.StoreKey, icahosttypes.StoreKey, ratelimitmoduletypes.StoreKey,
crisistypes.StoreKey, routertypes.StoreKey, transfermiddlewaretypes.StoreKey, group.StoreKey, minttypes.StoreKey, alliancemoduletypes.StoreKey, wasm.StoreKey, ibchookstypes.StoreKey, icahosttypes.StoreKey, ratelimitmoduletypes.StoreKey, txBoundaryTypes.StoreKey,
)

// Define transient store keys
Expand Down
21 changes: 21 additions & 0 deletions proto/centauri/txboundary/v1beta1/boundary.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
syntax = "proto3";
package centauri.txboundary.v1beta1;

import "gogoproto/gogo.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

option go_package = "x/tx-boundary/types";

// Boundary defines the number of tx limit and block per tx generation time
message Boundary {
uint64 tx_limit = 1;
int64 blocks_per_generation = 2;
}

// Boundary defines the number of delegate and redelegate per Addr
message LimitPerAddr {
uint64 delegate_count = 1;
uint64 reledegate_count = 2;
int64 latest_update_block = 3;
}
21 changes: 21 additions & 0 deletions proto/centauri/txboundary/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
syntax = "proto3";
package centauri.txboundary.v1beta1;

import "gogoproto/gogo.proto";
import "centauri/txboundary/v1beta1/boundary.proto";

option go_package = "x/tx-boundary/types";

// GenesisState defines the module various parameters when first
// initialized
message GenesisState {
Boundary delegate_boundary = 1 [
(gogoproto.moretags) = "yaml:\"delegate_boundary\"",
(gogoproto.nullable) = false
];
Boundary redelegate_boundary = 2 [
(gogoproto.moretags) = "yaml:\"redelegate_boundary\"",
(gogoproto.nullable) = false
];
}

40 changes: 40 additions & 0 deletions proto/centauri/txboundary/v1beta1/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
syntax = "proto3";
package centauri.txboundary.v1beta1;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "centauri/txboundary/v1beta1/boundary.proto";

option go_package = "x/tx-boundary/types";

// Query provides defines the gRPC querier service.
service Query {
// DelegateBoundary returns the boundary for the delegate tx.
rpc DelegateBoundary(QueryDelegateBoundaryRequest) returns (QueryDelegateBoundaryResponse) {
option (google.api.http).get = "/cosmos/txboundary/v1beta1/delegateboundary";
}

// RedelegateBoundary returns the boundary for the redelegate tx.
rpc RedelegateBoundary(QueryRedelegateBoundaryRequest) returns (QueryRedelegateBoundaryResponse) {
option (google.api.http).get = "/cosmos/txboundary/v1beta1/redelegateboundary";
}
}

// QueryDelegateBoundaryRequest is the request type for the Query/DelegateBoundary RPC method.
message QueryDelegateBoundaryRequest {}

// QueryDelegateBoundaryResponse is the response type for the Query/DelegateBoundary RPC method.
message QueryDelegateBoundaryResponse {
// boundary defines the boundary for the delegate tx
Boundary boundary = 1 [ (gogoproto.nullable) = false ];
}

// QueryRedelegateBoundaryRequest is the request type for the Query/ReDelegateBoundary RPC method.
message QueryRedelegateBoundaryRequest {}

// QueryRedelegateBoundaryResponse is the response type for the Query/ReDelegateBoundary RPC
// method.
message QueryRedelegateBoundaryResponse {
// boundary defines the boundary for the redelegate tx
Boundary boundary = 1 [ (gogoproto.nullable) = false ];
}
68 changes: 68 additions & 0 deletions proto/centauri/txboundary/v1beta1/tx.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
syntax = "proto3";
package centauri.txboundary.v1beta1;

import "cosmos/msg/v1/msg.proto";
import "amino/amino.proto";
import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "centauri/txboundary/v1beta1/boundary.proto";
import "cosmos/base/v1beta1/coin.proto";

option go_package = "x/tx-boundary/types";

// Msg defines the x/mint Msg service.
service Msg {
option (cosmos.msg.v1.service) = true;

rpc UpdateDelegateBoundary(MsgUpdateDelegateBoundary) returns (MsgUpdateDelegateBoundaryResponse);

rpc UpdateRedelegateBoundary(MsgUpdateRedelegateBoundary) returns (MsgUpdateRedelegateBoundaryResponse);
}

// MsgUpdateDelegateBoundary is the Msg/UpdateDelegateBoundary request type.
//
// Since: cosmos-sdk 0.47
message MsgUpdateDelegateBoundary {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "centauri/x/txboundary/MsgUpdateDelegateBoundary";

// authority is the address that controls the module (defaults to x/gov unless
// overwritten).
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// boundary defines the x/tx-boundary parameters to update.
//
// NOTE: All parameters must be supplied.
Boundary boundary = 2
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
}

// MsgUpdateDelegateBoundaryResponse defines the response structure for executing a
// MsgUpdateDelegateBoundary message.
//
// Since: cosmos-sdk 0.47
message MsgUpdateDelegateBoundaryResponse {}

// MsgUpdateRedelegateBoundary is the Msg/MsgUpdateRedelegateBoundary request type.
//
// Since: cosmos-sdk 0.47
message MsgUpdateRedelegateBoundary {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "centauri/x/txboundary/MsgUpdateDelegateBoundary";

// authority is the address that controls the module (defaults to x/gov unless
// overwritten).
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// boundary defines the x/tx-boundary parameters to update.
//
// NOTE: All parameters must be supplied.
Boundary boundary = 2
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
}

// MsgUpdateRedelegateBoundaryResponse defines the response structure for executing a
// MsgUpdateRedelegateBoundary message.
//
// Since: cosmos-sdk 0.47
message MsgUpdateRedelegateBoundaryResponse {}
Loading
Loading