diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e659bf81352..ae75085a189d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (baseapp) [#16290](https://github.com/cosmos/cosmos-sdk/pull/16290) Add circuit breaker setter in baseapp. * (x/group) [#16191](https://github.com/cosmos/cosmos-sdk/pull/16191) Add EventProposalPruned event to group module whenever a proposal is pruned. ### Improvements diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index c8c5ed14a63e..c58cc03adcfa 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -231,6 +231,15 @@ func (app *BaseApp) SetMsgServiceRouter(msgServiceRouter *MsgServiceRouter) { app.msgServiceRouter = msgServiceRouter } +// SetCircuitBreaker sets the circuit breaker for the BaseApp. +// The circuit breaker is checked on every message execution to verify if a transaction should be executed or not. +func (app *BaseApp) SetCircuitBreaker(cb CircuitBreaker) { + if app.msgServiceRouter == nil { + panic("must be called after message server is set") + } + app.msgServiceRouter.SetCircuit(cb) +} + // MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp // multistore. func (app *BaseApp) MountStores(keys ...storetypes.StoreKey) { diff --git a/baseapp/circuit.go b/baseapp/circuit.go new file mode 100644 index 000000000000..022ee6632c2e --- /dev/null +++ b/baseapp/circuit.go @@ -0,0 +1,10 @@ +package baseapp + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CircuitBreaker is an interface that defines the methods for a circuit breaker. +type CircuitBreaker interface { + IsAllowed(ctx sdk.Context, typeURL string) bool +} diff --git a/baseapp/msg_service_router.go b/baseapp/msg_service_router.go index 02192e892d8c..ff448086b8bd 100644 --- a/baseapp/msg_service_router.go +++ b/baseapp/msg_service_router.go @@ -17,6 +17,7 @@ import ( type MsgServiceRouter struct { interfaceRegistry codectypes.InterfaceRegistry routes map[string]MsgServiceHandler + circuitBreaker CircuitBreaker } var _ gogogrpc.Server = &MsgServiceRouter{} @@ -31,6 +32,10 @@ func NewMsgServiceRouter() *MsgServiceRouter { // MsgServiceHandler defines a function type which handles Msg service message. type MsgServiceHandler = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) +func (msr *MsgServiceRouter) SetCircuit(cb CircuitBreaker) { + msr.circuitBreaker = cb +} + // Handler returns the MsgServiceHandler for a given msg or nil if not found. func (msr *MsgServiceRouter) Handler(msg sdk.Msg) MsgServiceHandler { return msr.routes[sdk.MsgTypeURL(msg)] @@ -116,6 +121,13 @@ func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler inter if err := req.ValidateBasic(); err != nil { return nil, err } + + if msr.circuitBreaker != nil { + msgURL := sdk.MsgTypeURL(req) + if !msr.circuitBreaker.IsAllowed(ctx, msgURL) { + return nil, fmt.Errorf("circuit breaker disables execution of this message: %s", msgURL) + } + } // Call the method handler from the service description with the handler object. // We don't do any decoding here because the decoding was already done. res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), noopDecoder, interceptor)