Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 47e4682

Browse files
ValarDragoncwgoes
authored andcommitted
Merge PR cosmos#1415: x/stake: Limit the size of rationals from user input
* x/stake: Limit the size of rationals from user input This commit sets the maximum number of decimal points that can be passed in from messages. This is enforced on the validate basic of MsgBeginUnbonding and MsgBeginRedelegation. The cli has been updated to truncate the user input to the specified precision. This also updates types/rational to return big ints for Num() and Den(). Closes cosmos#887 * Switch NewFromDecimal to error instead of truncating
1 parent 0d28eda commit 47e4682

File tree

8 files changed

+71
-21
lines changed

8 files changed

+71
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ FIXES
6767
* \#1367 - set ChainID in InitChain
6868
* \#1353 - CLI: Show pool shares fractions in human-readable format
6969
* \#1258 - printing big.rat's can no longer overflow int64
70+
* \#887 - limit the size of rationals that can be passed in from user input
7071

7172
IMPROVEMENTS
7273
* bank module uses go-wire codec instead of 'encoding/json'

types/int.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ func (i Int) Neg() (res Int) {
227227
return Int{neg(i.i)}
228228
}
229229

230+
func (i Int) String() string {
231+
return i.i.String()
232+
}
233+
230234
// MarshalAmino defines custom encoding scheme
231235
func (i Int) MarshalAmino() (string, error) {
232236
if i.i == nil { // Necessary since default Uint initialization has i.i as nil

types/rational.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ func NewRat(Numerator int64, Denominator ...int64) Rat {
3838
}
3939

4040
// create a rational from decimal string or integer string
41-
func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
42-
41+
// precision is the number of values after the decimal point which should be read
42+
func NewRatFromDecimal(decimalStr string, prec int) (f Rat, err Error) {
4343
// first extract any negative symbol
4444
neg := false
4545
if string(decimalStr[0]) == "-" {
@@ -61,6 +61,9 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
6161
if len(str[0]) == 0 || len(str[1]) == 0 {
6262
return f, ErrUnknownRequest("not a decimal string")
6363
}
64+
if len(str[1]) > prec {
65+
return f, ErrUnknownRequest("string has too many decimals")
66+
}
6467
numStr = str[0] + str[1]
6568
len := int64(len(str[1]))
6669
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
@@ -69,8 +72,20 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
6972
}
7073

7174
num, errConv := strconv.Atoi(numStr)
72-
if errConv != nil {
73-
return f, ErrUnknownRequest(errConv.Error())
75+
if errConv != nil && strings.HasSuffix(errConv.Error(), "value out of range") {
76+
// resort to big int, don't make this default option for efficiency
77+
numBig, success := new(big.Int).SetString(numStr, 10)
78+
if success != true {
79+
return f, ErrUnknownRequest("not a decimal string")
80+
}
81+
82+
if neg {
83+
numBig.Neg(numBig)
84+
}
85+
86+
return NewRatFromBigInt(numBig, big.NewInt(denom)), nil
87+
} else if errConv != nil {
88+
return f, ErrUnknownRequest("not a decimal string")
7489
}
7590

7691
if neg {
@@ -105,9 +120,9 @@ func NewRatFromInt(num Int, denom ...Int) Rat {
105120
}
106121

107122
//nolint
108-
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
109-
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
110-
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
123+
func (r Rat) Num() Int { return Int{r.Rat.Num()} } // Num - return the numerator
124+
func (r Rat) Denom() Int { return Int{r.Rat.Denom()} } // Denom - return the denominator
125+
func (r Rat) IsZero() bool { return r.Num().IsZero() } // IsZero - Is the Rat equal to zero
111126
func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 }
112127
func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than
113128
func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal

types/rational_test.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func TestNew(t *testing.T) {
2121
}
2222

2323
func TestNewFromDecimal(t *testing.T) {
24+
largeBigInt, success := new(big.Int).SetString("3109736052979742687701388262607869", 10)
25+
require.True(t, success)
2426
tests := []struct {
2527
decimalStr string
2628
expErr bool
@@ -31,7 +33,13 @@ func TestNewFromDecimal(t *testing.T) {
3133
{"1.1", false, NewRat(11, 10)},
3234
{"0.75", false, NewRat(3, 4)},
3335
{"0.8", false, NewRat(4, 5)},
34-
{"0.11111", false, NewRat(11111, 100000)},
36+
{"0.11111", true, NewRat(1111, 10000)},
37+
{"628240629832763.5738930323617075341", true, NewRat(3141203149163817869, 5000)},
38+
{"621947210595948537540277652521.5738930323617075341",
39+
true, NewRatFromBigInt(largeBigInt, big.NewInt(5000))},
40+
{"628240629832763.5738", false, NewRat(3141203149163817869, 5000)},
41+
{"621947210595948537540277652521.5738",
42+
false, NewRatFromBigInt(largeBigInt, big.NewInt(5000))},
3543
{".", true, Rat{}},
3644
{".0", true, Rat{}},
3745
{"1.", true, Rat{}},
@@ -41,22 +49,21 @@ func TestNewFromDecimal(t *testing.T) {
4149
}
4250

4351
for _, tc := range tests {
44-
45-
res, err := NewRatFromDecimal(tc.decimalStr)
52+
res, err := NewRatFromDecimal(tc.decimalStr, 4)
4653
if tc.expErr {
4754
assert.NotNil(t, err, tc.decimalStr)
4855
} else {
49-
assert.Nil(t, err)
50-
assert.True(t, res.Equal(tc.exp))
56+
require.Nil(t, err, tc.decimalStr)
57+
require.True(t, res.Equal(tc.exp), tc.decimalStr)
5158
}
5259

5360
// negative tc
54-
res, err = NewRatFromDecimal("-" + tc.decimalStr)
61+
res, err = NewRatFromDecimal("-"+tc.decimalStr, 4)
5562
if tc.expErr {
5663
assert.NotNil(t, err, tc.decimalStr)
5764
} else {
58-
assert.Nil(t, err)
59-
assert.True(t, res.Equal(tc.exp.Mul(NewRat(-1))))
65+
assert.Nil(t, err, tc.decimalStr)
66+
assert.True(t, res.Equal(tc.exp.Mul(NewRat(-1))), tc.decimalStr)
6067
}
6168
}
6269
}
@@ -133,7 +140,7 @@ func TestArithmetic(t *testing.T) {
133140
assert.True(t, tc.resAdd.Equal(tc.r1.Add(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)
134141
assert.True(t, tc.resSub.Equal(tc.r1.Sub(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)
135142

136-
if tc.r2.Num() == 0 { // panic for divide by zero
143+
if tc.r2.Num().IsZero() { // panic for divide by zero
137144
assert.Panics(t, func() { tc.r1.Quo(tc.r2) })
138145
} else {
139146
assert.True(t, tc.resDiv.Equal(tc.r1.Quo(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)

x/stake/client/cli/tx.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/cosmos/cosmos-sdk/wire"
1313
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
1414
"github.com/cosmos/cosmos-sdk/x/stake"
15+
"github.com/cosmos/cosmos-sdk/x/stake/types"
1516
)
1617

1718
// create create validator command
@@ -219,7 +220,7 @@ func getShares(storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercent
219220
case sharesAmountStr == "" && sharesPercentStr == "":
220221
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
221222
case sharesAmountStr != "":
222-
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
223+
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr, types.MaxBondDenominatorPrecision)
223224
if err != nil {
224225
return sharesAmount, err
225226
}
@@ -228,7 +229,7 @@ func getShares(storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercent
228229
}
229230
case sharesPercentStr != "":
230231
var sharesPercent sdk.Rat
231-
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
232+
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr, types.MaxBondDenominatorPrecision)
232233
if err != nil {
233234
return sharesAmount, err
234235
}

x/stake/client/rest/tx.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
sdk "github.com/cosmos/cosmos-sdk/types"
1515
"github.com/cosmos/cosmos-sdk/wire"
1616
"github.com/cosmos/cosmos-sdk/x/stake"
17+
"github.com/cosmos/cosmos-sdk/x/stake/types"
1718
)
1819

1920
func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
@@ -145,7 +146,7 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
145146
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
146147
return
147148
}
148-
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
149+
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision)
149150
if err != nil {
150151
w.WriteHeader(http.StatusInternalServerError)
151152
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
@@ -210,7 +211,7 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
210211
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
211212
return
212213
}
213-
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
214+
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision)
214215
if err != nil {
215216
w.WriteHeader(http.StatusInternalServerError)
216217
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))

x/stake/types/errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ func ErrNotEnoughDelegationShares(codespace sdk.CodespaceType, shares string) sd
7979
func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error {
8080
return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0")
8181
}
82+
func ErrBadSharesPrecision(codespace sdk.CodespaceType) sdk.Error {
83+
return sdk.NewError(codespace, CodeInvalidDelegation,
84+
fmt.Sprintf("shares denominator must be < %s, try reducing the number of decimal points",
85+
maximumBondingRationalDenominator.String()),
86+
)
87+
}
8288
func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error {
8389
return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1")
8490
}

x/stake/types/msg.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
package types
22

33
import (
4+
"math"
5+
46
sdk "github.com/cosmos/cosmos-sdk/types"
57
"github.com/tendermint/tendermint/crypto"
68
)
79

810
// name to idetify transaction types
911
const MsgType = "stake"
1012

11-
//Verify interface at compile time
13+
// Maximum amount of decimal points in the decimal representation of rationals
14+
// used in MsgBeginUnbonding / MsgBeginRedelegate
15+
const MaxBondDenominatorPrecision = 8
16+
17+
// Verify interface at compile time
1218
var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}
1319
var _, _ sdk.Msg = &MsgBeginUnbonding{}, &MsgCompleteUnbonding{}
1420
var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{}
1521

22+
// Initialize Int for the denominator
23+
var maximumBondingRationalDenominator sdk.Int = sdk.NewInt(int64(math.Pow10(MaxBondDenominatorPrecision)))
24+
1625
//______________________________________________________________________
1726

1827
// MsgCreateValidator - struct for unbonding transactions
@@ -234,6 +243,9 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
234243
if msg.SharesAmount.LTE(sdk.ZeroRat()) {
235244
return ErrBadSharesAmount(DefaultCodespace)
236245
}
246+
if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) {
247+
return ErrBadSharesPrecision(DefaultCodespace)
248+
}
237249
return nil
238250
}
239251

@@ -340,6 +352,9 @@ func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error {
340352
if msg.SharesAmount.LTE(sdk.ZeroRat()) {
341353
return ErrBadSharesAmount(DefaultCodespace)
342354
}
355+
if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) {
356+
return ErrBadSharesPrecision(DefaultCodespace)
357+
}
343358
return nil
344359
}
345360

0 commit comments

Comments
 (0)