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

allow splitting withdrawal txs #4384

Merged
merged 11 commits into from May 23, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .pending/improvements/sdk/4384-WithdrawalTxSplitting
@@ -0,0 +1 @@
#4384- Allow splitting withdrawal transaction in several chunks
47 changes: 43 additions & 4 deletions x/distribution/client/cli/tx.go
Expand Up @@ -23,6 +23,11 @@ var (
flagOnlyFromValidator = "only-from-validator"
flagIsValidator = "is-validator"
flagComission = "commission"
flagMaxMessagesPerTx = "max-msgs"
)

const (
MaxMessagesPerTxDefault = 5
jleni marked this conversation as resolved.
Show resolved Hide resolved
)

// GetTxCmd returns the transaction commands for this module
Expand All @@ -40,6 +45,38 @@ func GetTxCmd(storeKey string, cdc *amino.Codec) *cobra.Command {
return distTxCmd
}

type generateOrBroadcastFunc func(context.CLIContext, authtxb.TxBuilder, []sdk.Msg) error

func splitAndApply(
generateOrBroadcast generateOrBroadcastFunc,
cliCtx context.CLIContext,
txBldr authtxb.TxBuilder,
msgs []sdk.Msg,
chunkSize int,
) error {

if chunkSize == 0 {
return generateOrBroadcast(cliCtx, txBldr, msgs)
}

// split messages into slices of length chunkSize
totalMessages := len(msgs)
for i := 0; i < len(msgs); i += chunkSize {

sliceEnd := i + chunkSize
if sliceEnd > totalMessages {
sliceEnd = totalMessages
}

msgChunk := msgs[i:sliceEnd]
if err := generateOrBroadcast(cliCtx, txBldr, msgChunk); err != nil {
return err
}
}

return nil
}

// command to withdraw rewards
func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Expand Down Expand Up @@ -77,7 +114,7 @@ $ <appcli> tx distr withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs

// command to withdraw all rewards
func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "withdraw-all-rewards",
Short: "withdraw all delegations rewards for a delegator",
Long: strings.TrimSpace(`Withdraw all rewards for a single delegator:
Expand All @@ -98,14 +135,17 @@ $ <appcli> tx distr withdraw-all-rewards --from mykey
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs)
chunkSize := viper.GetInt(flagMaxMessagesPerTx)
return splitAndApply(utils.GenerateOrBroadcastMsgs, cliCtx, txBldr, msgs, chunkSize)
},
}
cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)")
return cmd
}

// command to replace a delegator's withdrawal address
func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
return &cobra.Command{
Use: "set-withdraw-addr [withdraw-addr]",
Short: "change the default withdraw address for rewards associated with an address",
Long: strings.TrimSpace(`Set the withdraw address for rewards associated with a delegator address:
Expand All @@ -130,5 +170,4 @@ $ <appcli> tx set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
return cmd
}
77 changes: 77 additions & 0 deletions x/distribution/client/cli/tx_test.go
@@ -0,0 +1,77 @@
package cli

import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/crypto/secp256k1"
"testing"
)

func createFakeTxBuilder() authtxb.TxBuilder {
cdc := codec.New()
return authtxb.NewTxBuilder(
utils.GetTxEncoder(cdc),
123,
9876,
0,
1.2,
false,
"test_chain",
"hello",
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))),
sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))},
)
}

func Test_splitAndCall_NoMessages(t *testing.T) {
ctx := context.CLIContext{}
txBldr := createFakeTxBuilder()

err := splitAndApply(nil, ctx, txBldr, nil, 10)
assert.NoError(t, err, "")
}

func Test_splitAndCall_Splitting(t *testing.T) {
ctx := context.CLIContext{}
txBldr := createFakeTxBuilder()

addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())

// Add five messages
msgs := []sdk.Msg{
sdk.NewTestMsg(addr),
sdk.NewTestMsg(addr),
sdk.NewTestMsg(addr),
sdk.NewTestMsg(addr),
sdk.NewTestMsg(addr),
}

// Keep track of number of calls
const chunkSize = 2

callCount := 0
err := splitAndApply(
func(ctx context.CLIContext, txBldr authtxb.TxBuilder, msgs []sdk.Msg) error {
callCount += 1

assert.NotNil(t, ctx)
assert.NotNil(t, txBldr)
assert.NotNil(t, msgs)

if callCount < 3 {
assert.Equal(t, len(msgs), 2)
} else {
assert.Equal(t, len(msgs), 1)
}

return nil
},
ctx, txBldr, msgs, chunkSize)

assert.NoError(t, err, "")
assert.Equal(t, 3, callCount)
}