Skip to content

Commit

Permalink
chore: LSM changes to distribution (#4)
Browse files Browse the repository at this point in the history
* set min_self_delegation to 0 in TestAminoCodecFullDecodeAndEncode

* add WithdrawTokenizeShareRecordReward and WithdrawAllTokenizeShareRecordReward

* add methods to distribution/keeper

* register distribution msgs

* add SimulateMsgWithdrawTokenizeShareRecordReward

* LSM distribution queries

* LSM distr cli

* add BeforeTokenizeShareRecordRemoved hook

* add signers to proto distribution

* set signers correctly
  • Loading branch information
mpoke authored Nov 28, 2023
1 parent 2623645 commit 16938f1
Show file tree
Hide file tree
Showing 27 changed files with 1,198 additions and 250 deletions.
209 changes: 109 additions & 100 deletions api/cosmos/distribution/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion codec/amino_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestAminoCodecUnpackAnyFails(t *testing.T) {

func TestAminoCodecFullDecodeAndEncode(t *testing.T) {
// This tx comes from https://github.com/cosmos/cosmos-sdk/issues/8117.
txSigned := `{"type":"cosmos-sdk/StdTx","value":{"msg":[{"type":"cosmos-sdk/MsgCreateValidator","value":{"description":{"moniker":"fulltest","identity":"satoshi","website":"example.com","details":"example inc"},"commission":{"rate":"0.500000000000000000","max_rate":"1.000000000000000000","max_change_rate":"0.200000000000000000"},"min_self_delegation":"1000000","delegator_address":"cosmos14pt0q5cwf38zt08uu0n6yrstf3rndzr5057jys","validator_address":"cosmosvaloper14pt0q5cwf38zt08uu0n6yrstf3rndzr52q28gr","pubkey":{"type":"tendermint/PubKeyEd25519","value":"CYrOiM3HtS7uv1B1OAkknZnFYSRpQYSYII8AtMMtev0="},"value":{"denom":"umuon","amount":"700000000"}}}],"fee":{"amount":[{"denom":"umuon","amount":"6000"}],"gas":"160000"},"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AwAOXeWgNf1FjMaayrSnrOOKz+Fivr6DiI/i0x0sZCHw"},"signature":"RcnfS/u2yl7uIShTrSUlDWvsXo2p2dYu6WJC8VDVHMBLEQZWc8bsINSCjOnlsIVkUNNe1q/WCA9n3Gy1+0zhYA=="}],"memo":"","timeout_height":"0"}}`
txSigned := `{"type":"cosmos-sdk/StdTx","value":{"msg":[{"type":"cosmos-sdk/MsgCreateValidator","value":{"description":{"moniker":"fulltest","identity":"satoshi","website":"example.com","details":"example inc"},"commission":{"rate":"0.500000000000000000","max_rate":"1.000000000000000000","max_change_rate":"0.200000000000000000"},"min_self_delegation":"0","delegator_address":"cosmos14pt0q5cwf38zt08uu0n6yrstf3rndzr5057jys","validator_address":"cosmosvaloper14pt0q5cwf38zt08uu0n6yrstf3rndzr52q28gr","pubkey":{"type":"tendermint/PubKeyEd25519","value":"CYrOiM3HtS7uv1B1OAkknZnFYSRpQYSYII8AtMMtev0="},"value":{"denom":"umuon","amount":"700000000"}}}],"fee":{"amount":[{"denom":"umuon","amount":"6000"}],"gas":"160000"},"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AwAOXeWgNf1FjMaayrSnrOOKz+Fivr6DiI/i0x0sZCHw"},"signature":"RcnfS/u2yl7uIShTrSUlDWvsXo2p2dYu6WJC8VDVHMBLEQZWc8bsINSCjOnlsIVkUNNe1q/WCA9n3Gy1+0zhYA=="}],"memo":"","timeout_height":"0"}}`
legacyCdc := testutil.MakeTestEncodingConfig(staking.AppModuleBasic{}, auth.AppModuleBasic{}).Amino
var tx legacytx.StdTx
err := legacyCdc.UnmarshalJSON([]byte(txSigned), &tx)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/cosmos/gogoproto v1.4.10
github.com/cosmos/iavl v0.20.1
github.com/cosmos/ledger-cosmos-go v0.12.4
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.3
github.com/google/gofuzz v1.2.0
Expand Down Expand Up @@ -104,7 +105,6 @@ require (
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.1.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
Expand Down
6 changes: 6 additions & 0 deletions proto/cosmos/distribution/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ message MsgCommunityPoolSpend {

// MsgWithdrawTokenizeShareRecordReward withdraws tokenize share rewards for a specific record
message MsgWithdrawTokenizeShareRecordReward {
option (cosmos.msg.v1.signer) = "owner_address";
option (amino.name) = "cosmos-sdk/distr/MsgWithdrawTokenizeShareRecordReward";

option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

Expand All @@ -194,6 +197,9 @@ message MsgWithdrawTokenizeShareRecordRewardResponse {}
// MsgWithdrawAllTokenizeShareRecordReward withdraws tokenize share rewards or all
// records owned by the designated owner
message MsgWithdrawAllTokenizeShareRecordReward {
option (cosmos.msg.v1.signer) = "owner_address";
option (amino.name) = "cosmos-sdk/distr/MsgWithdrawAllTokenizeShareRecordReward";

option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

Expand Down
82 changes: 82 additions & 0 deletions tests/integration/distribution/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
"github.com/cosmos/cosmos-sdk/x/distribution/testutil"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/testutil"
Expand All @@ -35,6 +37,7 @@ type KeeperTestSuite struct {

interfaceRegistry codectypes.InterfaceRegistry
bankKeeper bankkeeper.Keeper
mintKeeper mintkeeper.Keeper
distrKeeper keeper.Keeper
stakingKeeper *stakingkeeper.Keeper
msgServer types.MsgServer
Expand All @@ -44,6 +47,7 @@ func (suite *KeeperTestSuite) SetupTest() {
app, err := simtestutil.Setup(testutil.AppConfig,
&suite.interfaceRegistry,
&suite.bankKeeper,
&suite.mintKeeper,
&suite.distrKeeper,
&suite.stakingKeeper,
)
Expand Down Expand Up @@ -673,6 +677,84 @@ func (suite *KeeperTestSuite) TestGRPCCommunityPool() {
}
}

func (suite *KeeperTestSuite) TestGRPCTokenizeShareRecordReward() {
ctx, queryClient := suite.ctx, suite.queryClient

addr := simtestutil.AddTestAddrs(suite.bankKeeper, suite.stakingKeeper, ctx, 2, sdk.NewInt(100000000))
valAddrs := simtestutil.ConvertAddrsToValAddrs(addr)
tstaking := stakingtestutil.NewHelper(suite.T(), ctx, suite.stakingKeeper)

// create validator with 50% commission
tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
valPower := int64(100)
tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true)

// end block to bond validator
staking.EndBlocker(ctx, suite.stakingKeeper)

// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)

// fetch validator and delegation
val := suite.stakingKeeper.Validator(ctx, valAddrs[0])
del := suite.stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0])

// end period
endingPeriod := suite.distrKeeper.IncrementValidatorPeriod(ctx, val)

// calculate delegation rewards
suite.distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)

// start out block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
val = suite.stakingKeeper.Validator(ctx, valAddrs[0])
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)

// allocate some rewards
initial := suite.stakingKeeper.TokensFromConsensusPower(ctx, 10)
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}
suite.distrKeeper.AllocateTokensToValidator(ctx, val, tokens)

// end period
suite.distrKeeper.IncrementValidatorPeriod(ctx, val)

coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial)}
err := suite.mintKeeper.MintCoins(ctx, coins)
suite.Require().NoError(err)

err = suite.bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, types.ModuleName, coins)
suite.Require().NoError(err)
// tokenize share amount
delTokens := sdk.NewInt(1000000)
msgServer := stakingkeeper.NewMsgServerImpl(suite.stakingKeeper)
_, err = msgServer.TokenizeShares(sdk.WrapSDKContext(ctx), &stakingtypes.MsgTokenizeShares{
DelegatorAddress: sdk.AccAddress(valAddrs[0]).String(),
ValidatorAddress: valAddrs[0].String(),
TokenizedShareOwner: sdk.AccAddress(valAddrs[0]).String(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, delTokens),
})
suite.Require().NoError(err)

staking.EndBlocker(ctx, suite.stakingKeeper)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
suite.distrKeeper.AllocateTokensToValidator(ctx, val, tokens)
suite.distrKeeper.IncrementValidatorPeriod(ctx, val)

rewards, err := queryClient.TokenizeShareRecordReward(gocontext.Background(), &types.QueryTokenizeShareRecordRewardRequest{
OwnerAddress: sdk.AccAddress(valAddrs[0]).String(),
})
suite.Require().NoError(err)
suite.Require().Equal(&types.QueryTokenizeShareRecordRewardResponse{
Rewards: []types.TokenizeShareRecordReward{
{
RecordId: 1,
Reward: sdk.DecCoins{sdk.NewInt64DecCoin("stake", 50000)},
},
},
Total: sdk.DecCoins{sdk.NewInt64DecCoin("stake", 50000)},
}, rewards)
}

func TestDistributionTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
46 changes: 46 additions & 0 deletions x/distribution/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func GetQueryCmd() *cobra.Command {
GetCmdQueryValidatorSlashes(),
GetCmdQueryDelegatorRewards(),
GetCmdQueryCommunityPool(),
GetCmdQueryTokenizeShareRecordReward(),
)

return distQueryCmd
Expand Down Expand Up @@ -364,3 +365,48 @@ $ %s query distribution community-pool
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

// GetCmdQueryTokenizeShareRecordReward implements the query tokenize share record rewards
func GetCmdQueryTokenizeShareRecordReward() *cobra.Command {
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()

cmd := &cobra.Command{
Use: "tokenize-share-record-rewards [owner]",
Args: cobra.ExactArgs(1),
Short: "Query distribution tokenize share record rewards",
Long: strings.TrimSpace(
fmt.Sprintf(`Query the query tokenize share record rewards.
Example:
$ %s query distribution tokenize-share-record-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
`,
version.AppName, bech32PrefixAccAddr,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

ownerAddr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}

res, err := queryClient.TokenizeShareRecordReward(
cmd.Context(),
&types.QueryTokenizeShareRecordRewardRequest{OwnerAddress: ownerAddr.String()},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)
return cmd
}
48 changes: 48 additions & 0 deletions x/distribution/client/cli/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,51 @@ func (s *CLITestSuite) TestNewFundCommunityPoolCmd() {
})
}
}

func (s *CLITestSuite) TestNewWithdrawAllTokenizeShareRecordRewardCmd() {
val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1)

testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
{
"valid transaction of withdraw tokenize share record reward",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()),
},
false, 0, &sdk.TxResponse{},
},
}

for _, tc := range testCases {
tc := tc

s.Run(tc.name, func() {
cmd := cli.NewWithdrawAllTokenizeShareRecordRewardCmd()

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

txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
}

// This test requires multiple validators, if I add this test to `IntegrationTestSuite` by increasing
// `NumValidators` the existing tests are leading to non-determnism so created new suite for this test.
func (s *CLITestSuite) TestNewWithdrawAllRewardsGenerateOnly() {
// TODO add LSM test
}
72 changes: 72 additions & 0 deletions x/distribution/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -40,6 +41,8 @@ func NewTxCmd() *cobra.Command {
NewWithdrawAllRewardsCmd(),
NewSetWithdrawAddrCmd(),
NewFundCommunityPoolCmd(),
NewWithdrawTokenizeShareRecordRewardCmd(),
NewWithdrawAllTokenizeShareRecordRewardCmd(),
)

return distTxCmd
Expand Down Expand Up @@ -254,3 +257,72 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey

return cmd
}

// WithdrawAllTokenizeShareRecordReward defines a method to withdraw reward for all owning TokenizeShareRecord
func NewWithdrawAllTokenizeShareRecordRewardCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-all-tokenize-share-rewards",
Args: cobra.ExactArgs(0),
Short: "Withdraw reward for all owning TokenizeShareRecord",
Long: strings.TrimSpace(
fmt.Sprintf(`Withdraw reward for all owned TokenizeShareRecord
Example:
$ %s tx distribution withdraw-tokenize-share-rewards --from mykey
`,
version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.NewMsgWithdrawAllTokenizeShareRecordReward(clientCtx.GetFromAddress())

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

flags.AddTxFlagsToCmd(cmd)

return cmd
}

// WithdrawTokenizeShareRecordReward defines a method to withdraw reward for an owning TokenizeShareRecord
func NewWithdrawTokenizeShareRecordRewardCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-tokenize-share-rewards",
Args: cobra.ExactArgs(1),
Short: "Withdraw reward for an owning TokenizeShareRecord",
Long: strings.TrimSpace(
fmt.Sprintf(`Withdraw reward for an owned TokenizeShareRecord
Example:
$ %s tx distribution withdraw-tokenize-share-rewards 1 --from mykey
`,
version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

recordID, err := strconv.Atoi(args[0])
if err != nil {
return err
}

msg := types.NewMsgWithdrawTokenizeShareRecordReward(clientCtx.GetFromAddress(), uint64(recordID))

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

flags.AddTxFlagsToCmd(cmd)

return cmd
}
4 changes: 4 additions & 0 deletions x/distribution/keeper/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ func TestCalculateRewardsBasic(t *testing.T) {
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission)
}

func TestWithdrawTokenizeShareRecordReward(t *testing.T) {
// TODO add LSM test
}

func TestCalculateRewardsAfterSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := sdk.NewKVStoreKey(disttypes.StoreKey)
Expand Down
Loading

0 comments on commit 16938f1

Please sign in to comment.