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

Refactor x/{gov, crisis} according to ADR 031 #7533

Merged
merged 17 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions proto/cosmos/crisis/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/crisis/types";

import "gogoproto/gogo.proto";

// Msg defines the bank Msg service.
service Msg {
// VerifyInvariant defines a method to verify a particular invariance.
rpc VerifyInvariant(MsgVerifyInvariant) returns (MsgVerifyInvariantResponse);
}

// MsgVerifyInvariant represents a message to verify a particular invariance.
message MsgVerifyInvariant {
option (gogoproto.equal) = false;
Expand All @@ -14,3 +20,6 @@ message MsgVerifyInvariant {
string invariant_module_name = 2 [(gogoproto.moretags) = "yaml:\"invariant_module_name\""];
string invariant_route = 3 [(gogoproto.moretags) = "yaml:\"invariant_route\""];
}

// MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type.
message MsgVerifyInvariantResponse { }
aaronc marked this conversation as resolved.
Show resolved Hide resolved
37 changes: 33 additions & 4 deletions proto/cosmos/gov/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,26 @@ import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";

option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types";
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = false;
option (gogoproto.goproto_getters_all) = false;

// Msg defines the bank Msg service.
service Msg {
// SubmitProposal defines a method to create new proposal given a content.
rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse);

// Vote defines a method to add a vote on a specific proposal.
rpc Vote(MsgVote) returns (MsgVoteResponse);

// Deposit defines a method to add deposit on a specific proposal.
rpc Deposit(MsgDeposit) returns (MsgDepositResponse);
}

// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary
// proposal Content.
message MsgSubmitProposal {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.stringer) = false;
option (gogoproto.goproto_getters) = false;

google.protobuf.Any content = 1 [(cosmos_proto.accepts_interface) = "Content"];
repeated cosmos.base.v1beta1.Coin initial_deposit = 2 [
Expand All @@ -26,21 +38,38 @@ message MsgSubmitProposal {
string proposer = 3;
}

// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type.
message MsgSubmitProposalResponse {
uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""];
}

// MsgVote defines a message to cast a vote.
message MsgVote {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.stringer) = false;
option (gogoproto.goproto_getters) = false;

uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""];
string voter = 2;
VoteOption option = 3;
}

// MsgVoteResponse defines the Msg/Vote response type.
message MsgVoteResponse {}

// MsgDeposit defines a message to submit a deposit to an existing proposal.
message MsgDeposit {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.stringer) = false;
option (gogoproto.goproto_getters) = false;

uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""];
string depositor = 2;
repeated cosmos.base.v1beta1.Coin amount = 3
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
}
}

// MsgDepositResponse defines the Msg/Deposit response type.
message MsgDepositResponse {}
73 changes: 3 additions & 70 deletions x/crisis/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,23 @@ package crisis
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/crisis/keeper"
"github.com/cosmos/cosmos-sdk/x/crisis/types"
)

// RouterKey
const RouterKey = types.ModuleName

func NewHandler(k keeper.Keeper) sdk.Handler {
func NewHandler(k types.MsgServer) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())

switch msg := msg.(type) {
case *types.MsgVerifyInvariant:
return handleMsgVerifyInvariant(ctx, msg, k)
res, err := k.VerifyInvariant(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)

default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized crisis message type: %T", msg)
}
}
}

func handleMsgVerifyInvariant(ctx sdk.Context, msg *types.MsgVerifyInvariant, k keeper.Keeper) (*sdk.Result, error) {
constantFee := sdk.NewCoins(k.GetConstantFee(ctx))

sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, err
}
if err := k.SendCoinsFromAccountToFeeCollector(ctx, sender, constantFee); err != nil {
return nil, err
}

// use a cached context to avoid gas costs during invariants
cacheCtx, _ := ctx.CacheContext()

found := false
msgFullRoute := msg.FullInvariantRoute()

var res string
var stop bool
for _, invarRoute := range k.Routes() {
if invarRoute.FullRoute() == msgFullRoute {
res, stop = invarRoute.Invar(cacheCtx)
found = true

break
}
}

if !found {
return nil, types.ErrUnknownInvariant
}

if stop {
// NOTE currently, because the chain halts here, this transaction will never be included
// in the blockchain thus the constant fee will have never been deducted. Thus no
// refund is required.

// TODO uncomment the following code block with implementation of the circuit breaker
//// refund constant fee
//err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender)
//if err != nil {
//// if there are insufficient coins to refund, log the error,
//// but still halt the chain.
//logger := ctx.Logger().With("module", "x/crisis")
//logger.Error(fmt.Sprintf(
//"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err))
//}

// TODO replace with circuit breaker
panic(res)
}

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeInvariant,
sdk.NewAttribute(types.AttributeKeyRoute, msg.InvariantRoute),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCrisis),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
),
})

return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
}
78 changes: 78 additions & 0 deletions x/crisis/keeper/msg_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/crisis/types"
)

var _ types.MsgServer = Keeper{}

func (k Keeper) VerifyInvariant(goCtx context.Context, msg *types.MsgVerifyInvariant) (*types.MsgVerifyInvariantResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
constantFee := sdk.NewCoins(k.GetConstantFee(ctx))

sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, err
}
if err := k.SendCoinsFromAccountToFeeCollector(ctx, sender, constantFee); err != nil {
return nil, err
}

// use a cached context to avoid gas costs during invariants
cacheCtx, _ := ctx.CacheContext()

found := false
msgFullRoute := msg.FullInvariantRoute()

var res string
var stop bool
for _, invarRoute := range k.Routes() {
if invarRoute.FullRoute() == msgFullRoute {
res, stop = invarRoute.Invar(cacheCtx)
found = true

break
}
}

if !found {
return nil, types.ErrUnknownInvariant
}

if stop {
// NOTE currently, because the chain halts here, this transaction will never be included
// in the blockchain thus the constant fee will have never been deducted. Thus no
// refund is required.

// TODO uncomment the following code block with implementation of the circuit breaker
//// refund constant fee
//err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender)
//if err != nil {
//// if there are insufficient coins to refund, log the error,
//// but still halt the chain.
//logger := ctx.Logger().With("module", "x/crisis")
//logger.Error(fmt.Sprintf(
//"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err))
//}

// TODO replace with circuit breaker
panic(res)
}

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeInvariant,
sdk.NewAttribute(types.AttributeKeyRoute, msg.InvariantRoute),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCrisis),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
),
})

return &types.MsgVerifyInvariantResponse{}, nil
}
Loading