Unregistered message handler
In the legacy version of the Msg Service
, all messages have to be registered in a module keeper's NewHandler
method. Failing to do so would prevent users from sending the not-registered message.
In the recent Cosmos version manual registration is no longer needed.
Example
There is one message handler missing.
service Msg {
rpc ConfirmBatch(MsgConfirmBatch) returns (MsgConfirmBatchResponse) {
option (google.api.http).post = "/gravity/v1/confirm_batch";
}
rpc UpdateCall(MsgUpdateCall) returns (MsgUpdateCallResponse) {
option (google.api.http).post = "/gravity/v1/update_call";
}
rpc CancelCall(MsgCancelCall) returns (MsgCancelCallResponse) {
option (google.api.http).post = "/gravity/v1/cancel_call";
}
rpc SetCall(MsgSetCall) returns (MsgSetCallResponse) {
option (google.api.http).post = "/gravity/v1/set_call";
}
rpc SendCall(MsgSendCall) returns (MsgSendCallResponse) {
option (google.api.http).post = "/gravity/v1/send_call";
}
rpc SetUserAddress(MsgSetUserAddress) returns (MsgSetUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/set_useraddress";
}
rpc SendUserAddress(MsgSendUserAddress) returns (MsgSendUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/send_useraddress";
}
rpc RequestBatch(MsgRequestBatch) returns (MsgRequestBatchResponse) {
option (google.api.http).post = "/gravity/v1/request_batch";
}
rpc RequestCall(MsgRequestCall) returns (MsgRequestCallResponse) {
option (google.api.http).post = "/gravity/v1/request_call";
}
rpc RequestUserAddress(MsgRequestUserAddress) returns (MsgRequestUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/request_useraddress";
}
rpc ConfirmEthClaim(MsgConfirmEthClaim) returns (MsgConfirmEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/confirm_ethclaim";
}
rpc UpdateEthClaim(MsgUpdateEthClaim) returns (MsgUpdateEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/update_ethclaim";
}
rpc SetBatch(MsgSetBatch) returns (MsgSetBatchResponse) {
option (google.api.http).post = "/gravity/v1/set_batch";
}
rpc SendBatch(MsgSendBatch) returns (MsgSendBatchResponse) {
option (google.api.http).post = "/gravity/v1/send_batch";
}
rpc CancelUserAddress(MsgCancelUserAddress) returns (MsgCancelUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/cancel_useraddress";
}
rpc CancelEthClaim(MsgCancelEthClaim) returns (MsgCancelEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/cancel_ethclaim";
}
rpc RequestEthClaim(MsgRequestEthClaim) returns (MsgRequestEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/request_ethclaim";
}
rpc UpdateBatch(MsgUpdateBatch) returns (MsgUpdateBatchResponse) {
option (google.api.http).post = "/gravity/v1/update_batch";
}
rpc SendEthClaim(MsgSendEthClaim) returns (MsgSendEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/send_ethclaim";
}
rpc SetEthClaim(MsgSetEthClaim) returns (MsgSetEthClaimResponse) {
option (google.api.http).post = "/gravity/v1/set_ethclaim";
}
rpc CancelBatch(MsgCancelBatch) returns (MsgCancelBatchResponse) {
option (google.api.http).post = "/gravity/v1/cancel_batch";
}
rpc UpdateUserAddress(MsgUpdateUserAddress) returns (MsgUpdateUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/update_useraddress";
}
rpc ConfirmCall(MsgConfirmCall) returns (MsgConfirmCallResponse) {
option (google.api.http).post = "/gravity/v1/confirm_call";
}
rpc ConfirmUserAddress(MsgConfirmUserAddress) returns (MsgConfirmUserAddressResponse) {
option (google.api.http).post = "/gravity/v1/confirm_useraddress";
}
}
func NewHandler(k keeper.Keeper) sdk.Handler {
msgServer := keeper.NewMsgServerImpl(k)
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
switch msg := msg.(type) {
case *types.MsgSetBatch:
res, err := msgServer.SetBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUpdateUserAddress:
res, err := msgServer.UpdateUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUpdateCall:
res, err := msgServer.UpdateCall(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSendBatch:
res, err := msgServer.SendBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgCancelUserAddress:
res, err := msgServer.CancelUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgRequestBatch:
res, err := msgServer.RequestBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUpdateEthClaim:
res, err := msgServer.UpdateEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSendCall:
res, err := msgServer.SendCall(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSetCall:
res, err := msgServer.SetCall(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgCancelEthClaim:
res, err := msgServer.CancelEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgConfirmEthClaim:
res, err := msgServer.ConfirmEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgConfirmCall:
res, err := msgServer.ConfirmCall(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgRequestCall:
res, err := msgServer.RequestCall(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgConfirmUserAddress:
res, err := msgServer.ConfirmUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgRequestUserAddress:
res, err := msgServer.RequestUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSendEthClaim:
res, err := msgServer.SendEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSetEthClaim:
res, err := msgServer.SetEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgCancelBatch:
res, err := msgServer.CancelBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSetUserAddress:
res, err := msgServer.SetUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgRequestEthClaim:
res, err := msgServer.RequestEthClaim(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgConfirmBatch:
res, err := msgServer.ConfirmBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUpdateBatch:
res, err := msgServer.UpdateBatch(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgSendUserAddress:
res, err := msgServer.SendUserAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized Gravity Msg type: %v", msg.Type()))
}
}
}
And it is the CancelCall
msg.
Mitigations
- Use the recent Msg Service mechanism
- Test all functionalities
- Deploy static-analysis tests in CI pipeline for all manually maintained code that must be repeated in multiple files/methods
External examples
- The bug occurred in the Gravity Bridge. It was impossible to send evidence of malicious behavior, which impacted Gravity Bridge's security model.