package keeper import ( "bytes" "context" "fmt" "strings" "cosmossdk.io/collections" "cosmossdk.io/core/event" errorsmod "cosmossdk.io/errors" "cosmossdk.io/x/circuit/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) type msgServer struct { Keeper } var _ types.MsgServer = msgServer{} // NewMsgServerImpl returns an implementation of the circuit MsgServer interface // for the provided Keeper. func NewMsgServerImpl(keeper Keeper) types.MsgServer { return &msgServer{Keeper: keeper} } func (srv msgServer) AuthorizeCircuitBreaker(ctx context.Context, msg *types.MsgAuthorizeCircuitBreaker) (*types.MsgAuthorizeCircuitBreakerResponse, error) { address, err := srv.addressCodec.StringToBytes(msg.Granter) if err != nil { return nil, err } // if the granter is the module authority no need to check perms if !bytes.Equal(address, srv.GetAuthority()) { // Check that the authorizer has the permission level of "super admin" perms, err := srv.Permissions.Get(ctx, address) if err != nil { if errorsmod.IsOf(err, collections.ErrNotFound) { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only super admins can authorize users") } return nil, err } if perms.Level != types.Permissions_LEVEL_SUPER_ADMIN { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only super admins can authorize users") } } grantee, err := srv.addressCodec.StringToBytes(msg.Grantee) if err != nil { return nil, err } if msg.Permissions == nil { return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "permissions cannot be nil") } // Append the account in the msg to the store's set of authorized super admins if err = srv.Permissions.Set(ctx, grantee, *msg.Permissions); err != nil { return nil, err } if err = srv.Keeper.EventService.EventManager(ctx).EmitKV( "authorize_circuit_breaker", event.NewAttribute("granter", msg.Granter), event.NewAttribute("grantee", msg.Grantee), event.NewAttribute("permission", msg.Permissions.String()), ); err != nil { return nil, err } return &types.MsgAuthorizeCircuitBreakerResponse{ Success: true, }, nil } func (srv msgServer) TripCircuitBreaker(ctx context.Context, msg *types.MsgTripCircuitBreaker) (*types.MsgTripCircuitBreakerResponse, error) { address, err := srv.addressCodec.StringToBytes(msg.Authority) if err != nil { return nil, err } // Check that the account has the permissions perms, err := srv.Permissions.Get(ctx, address) if err != nil && !errorsmod.IsOf(err, collections.ErrNotFound) { return nil, err } for _, msgTypeURL := range msg.MsgTypeUrls { // check if the message is in the list of allowed messages isAllowed, err := srv.IsAllowed(ctx, msgTypeURL) if err != nil { return nil, err } if !isAllowed { return nil, fmt.Errorf("message %s is already disabled", msgTypeURL) } switch { case perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || bytes.Equal(address, srv.GetAuthority()): // if the sender is a super admin or the module authority, no need to check perms case perms.Level == types.Permissions_LEVEL_SOME_MSGS: // if the sender has permission for some messages, check if the sender has permission for this specific message if !hasPermissionForMsg(perms, msgTypeURL) { return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker for message %s", msgTypeURL) } default: return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker") } if err = srv.DisableList.Set(ctx, msgTypeURL); err != nil { return nil, err } } urls := strings.Join(msg.GetMsgTypeUrls(), ",") if err = srv.Keeper.EventService.EventManager(ctx).EmitKV( "trip_circuit_breaker", event.NewAttribute("authority", msg.Authority), event.NewAttribute("msg_url", urls), ); err != nil { return nil, err } return &types.MsgTripCircuitBreakerResponse{ Success: true, }, nil } // ResetCircuitBreaker resumes processing of Msg's in the state machine that // have been paused using TripCircuitBreaker. func (srv msgServer) ResetCircuitBreaker(ctx context.Context, msg *types.MsgResetCircuitBreaker) (*types.MsgResetCircuitBreakerResponse, error) { keeper := srv.Keeper address, err := srv.addressCodec.StringToBytes(msg.Authority) if err != nil { return nil, err } // Get the permissions for the account specified in the msg.Authority field perms, err := keeper.Permissions.Get(ctx, address) if err != nil && !errorsmod.IsOf(err, collections.ErrNotFound) { return nil, err } for _, msgTypeURL := range msg.MsgTypeUrls { // check if the message is in the list of allowed messages isAllowed, err := srv.IsAllowed(ctx, msgTypeURL) if err != nil { return nil, err } if isAllowed { return nil, fmt.Errorf("message %s is not disabled", msgTypeURL) } switch { case perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || bytes.Equal(address, srv.GetAuthority()): // if the sender is a super admin or the module authority, no need to check perms case perms.Level == types.Permissions_LEVEL_SOME_MSGS: // if the sender has permission for some messages, check if the sender has permission for this specific message if !hasPermissionForMsg(perms, msgTypeURL) { return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "account does not have permission to reset circuit breaker for message %s", msgTypeURL) } default: return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to reset circuit breaker") } if err = srv.DisableList.Remove(ctx, msgTypeURL); err != nil { return nil, err } } urls := strings.Join(msg.GetMsgTypeUrls(), ",") if err = srv.Keeper.EventService.EventManager(ctx).EmitKV( "reset_circuit_breaker", event.NewAttribute("authority", msg.Authority), event.NewAttribute("msg_url", urls), ); err != nil { return nil, err } return &types.MsgResetCircuitBreakerResponse{Success: true}, nil } // hasPermissionForMsg returns true if the account can trip or reset the message. func hasPermissionForMsg(perms types.Permissions, msg string) bool { for _, msgurl := range perms.LimitTypeUrls { if msg == msgurl { return true } } return false }