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

feat: add msg for permanent locked vesting accounts #11019

Merged
merged 6 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#10507](https://github.com/cosmos/cosmos-sdk/pull/10507) Add middleware for tx priority.
* [\#10311](https://github.com/cosmos/cosmos-sdk/pull/10311) Adds cli to use tips transactions. It adds an `--aux` flag to all CLI tx commands to generate the aux signer data (with optional tip), and a new `tx aux-to-fee` subcommand to let the fee payer gather aux signer data and broadcast the tx
* [\#10430](https://github.com/cosmos/cosmos-sdk/pull/10430) ADR-040: Add store/v2 `MultiStore` implementation
* [\#11019](https://github.com/cosmos/cosmos-sdk/pull/11019) Add `MsgCreatePermanentLockedAccount` and CLI method for creating permanent locked account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add a state-machine breaking one

* [\#10947](https://github.com/cosmos/cosmos-sdk/pull/10947) Add `AllowancesByGranter` query to the feegrant module

### API Breaking Changes
Expand Down
1,257 changes: 1,186 additions & 71 deletions api/cosmos/vesting/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions api/cosmos/vesting/v1beta1/tx_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 4 additions & 6 deletions orm/internal/testpb/bank.pulsar.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ package testpb

import (
fmt "fmt"
io "io"
reflect "reflect"
sync "sync"

runtime "github.com/cosmos/cosmos-proto/runtime"
_ "github.com/cosmos/cosmos-sdk/api/cosmos/orm/v1alpha1"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoiface "google.golang.org/protobuf/runtime/protoiface"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"

_ "github.com/cosmos/cosmos-sdk/api/cosmos/orm/v1alpha1"
io "io"
reflect "reflect"
sync "sync"
)

var (
Expand Down
18 changes: 18 additions & 0 deletions proto/cosmos/vesting/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ service Msg {
// CreateVestingAccount defines a method that enables creating a vesting
// account.
rpc CreateVestingAccount(MsgCreateVestingAccount) returns (MsgCreateVestingAccountResponse);
// CreatePermanentLockedAccount defines a method that enables creating a permanent
// locked account.
rpc CreatePermanentLockedAccount(MsgCreatePermanentLockedAccount) returns (MsgCreatePermanentLockedAccountResponse);
// CreatePeriodicVestingAccount defines a method that enables creating a
// periodic vesting account.
rpc CreatePeriodicVestingAccount(MsgCreatePeriodicVestingAccount) returns (MsgCreatePeriodicVestingAccountResponse);
Expand All @@ -35,6 +38,21 @@ message MsgCreateVestingAccount {
// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type.
message MsgCreateVestingAccountResponse {}


// MsgCreatePermanentLockedAccount defines a message that enables creating a permanent
// locked account.
message MsgCreatePermanentLockedAccount {
option (gogoproto.equal) = true;

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

// MsgCreatePermanentLockedAccountResponse defines the Msg/CreatePermanentLockedAccount response type.
message MsgCreatePermanentLockedAccountResponse {}

// MsgCreateVestingAccount defines a message that enables creating a vesting
// account.
message MsgCreatePeriodicVestingAccount {
Expand Down
37 changes: 37 additions & 0 deletions x/auth/vesting/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func GetTxCmd() *cobra.Command {

txCmd.AddCommand(
NewMsgCreateVestingAccountCmd(),
NewMsgCreatePermanentLockedAccountCmd(),
NewMsgCreatePeriodicVestingAccountCmd(),
)

Expand Down Expand Up @@ -84,6 +85,42 @@ timestamp.`,
return cmd
}

// NewMsgCreatePermanentLockedAccountCmd returns a CLI command handler for creating a
// MsgCreatePermanentLockedAccount transaction.
func NewMsgCreatePermanentLockedAccountCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create-permanent-locked-account [to_address] [amount]",
Short: "Create a new permanently locked account funded with an allocation of tokens.",
Long: `Create a new account funded with an allocation of permanently locked tokens. These
tokens may be used for staking but are non-transferable. Staking rewards will acrue as liquid and transferable
tokens.`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
toAddr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}

amount, err := sdk.ParseCoinsNormalized(args[1])
if err != nil {
return err
}

msg := types.NewMsgCreatePermanentLockedAccount(clientCtx.GetFromAddress(), toAddr, amount)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

type VestingData struct {
StartTime int64 `json:"start_time"`
Periods []InputPeriod `json:"periods"`
Expand Down
79 changes: 79 additions & 0 deletions x/auth/vesting/client/testutil/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,82 @@ func (s *IntegrationTestSuite) TestNewMsgCreateVestingAccountCmd() {
s.T().Logf("Height now: %d", height)
}
}

func (s *IntegrationTestSuite) TestNewMsgCreatePermanentLockedAccountCmd() {
val := s.network.Validators[0]

testCases := map[string]struct {
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
"create a permanent locked account": {
args: []string{
sdk.AccAddress("addr4_______________").String(),
sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(100))).String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
expectErr: false,
expectedCode: 0,
respType: &sdk.TxResponse{},
},
"invalid address": {
args: []string{
"addr4",
sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String(),
"4070908800",
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address),
},
expectErr: true,
expectedCode: 0,
respType: &sdk.TxResponse{},
},
"invalid coins": {
args: []string{
sdk.AccAddress("addr4_______________").String(),
"fooo",
"4070908800",
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address),
},
expectErr: true,
expectedCode: 0,
respType: &sdk.TxResponse{},
},
}

// Synchronize height between test runs, to ensure sequence numbers are
// properly updated.
height, err := s.network.LatestHeight()
if err != nil {
s.T().Fatalf("Getting initial latest height: %v", err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err != nil {
s.T().Fatalf("Getting initial latest height: %v", err)
}
s.Require().NoError(err, "Getting initial latest height")

s.T().Logf("Initial latest height: %d", height)
for name, tc := range testCases {
tc := tc

s.Run(name, func() {
clientCtx := val.ClientCtx

bw, err := clitestutil.ExecTestCLICmd(clientCtx, cli.NewMsgCreatePermanentLockedAccountCmd(), tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(bw.Bytes(), tc.respType), bw.String())

txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code)
}
})
next, err := s.network.WaitForHeight(height + 1)
if err != nil {
s.T().Fatalf("Waiting for height %d: %v", height+1, err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above, let's use require

height = next
s.T().Logf("Height now: %d", height)
}
}
66 changes: 66 additions & 0 deletions x/auth/vesting/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,72 @@ func (s msgServer) CreateVestingAccount(goCtx context.Context, msg *types.MsgCre
return &types.MsgCreateVestingAccountResponse{}, nil
}

func (s msgServer) CreatePermanentLockedAccount(goCtx context.Context, msg *types.MsgCreatePermanentLockedAccount) (*types.MsgCreatePermanentLockedAccountResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
ak := s.AccountKeeper
bk := s.BankKeeper

if err := bk.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
return nil, err
}

from, err := sdk.AccAddressFromBech32(msg.FromAddress)
if err != nil {
return nil, err
}
to, err := sdk.AccAddressFromBech32(msg.ToAddress)
if err != nil {
return nil, err
}

if bk.BlockedAddr(to) {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}

if acc := ak.GetAccount(ctx, to); acc != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
}

baseAccountI := ak.NewAccountWithAddress(ctx, to)

baseAcc, ok := baseAccountI.(*authtypes.BaseAccount)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid account type; expected: BaseAccount, got: %T", baseAccountI)
}

var acc authtypes.AccountI = types.NewPermanentLockedAccount(baseAcc, msg.Amount)

ak.SetAccount(ctx, acc)

defer func() {
telemetry.IncrCounter(1, "new", "account")

for _, a := range msg.Amount {
if a.Amount.IsInt64() {
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", "create_permanent_locked_account"},
float32(a.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
)
}
}
}()

err = bk.SendCoins(ctx, from, to, msg.Amount)
if err != nil {
return nil, err
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
)

return &types.MsgCreatePermanentLockedAccountResponse{}, nil
}

func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *types.MsgCreatePeriodicVestingAccount) (*types.MsgCreatePeriodicVestingAccountResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

Expand Down
3 changes: 3 additions & 0 deletions x/auth/vesting/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil)
cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil)
cdc.RegisterConcrete(&PermanentLockedAccount{}, "cosmos-sdk/PermanentLockedAccount", nil)
cdc.RegisterConcrete(&MsgCreateVestingAccount{}, "cosmos-sdk/MsgCreateVestingAccount", nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this line mean there is no Amino JSON signing support for MsgCreateVestingAccount in 0.44 and 0.45?

Copy link
Contributor

@amaury1093 amaury1093 Apr 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I think it would still work in 0.44 and 0.45, but be shown without the type and value fields, similarly to cosmos/cosmjs#1026 (comment)

cdc.RegisterConcrete(&MsgCreatePermanentLockedAccount{}, "cosmos-sdk/MsgCreatePermLockedAccount", nil)
}

// RegisterInterface associates protoName with AccountI and VestingAccount
Expand Down Expand Up @@ -53,6 +55,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreateVestingAccount{},
&MsgCreatePermanentLockedAccount{},
)

msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
Expand Down
Loading