Merge PR #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 #887 * Switch NewFromDecimal to error instead of truncating
This commit is contained in:
committed by
Christopher Goes
parent
0d28eda146
commit
47e4682d9f
@@ -227,6 +227,10 @@ func (i Int) Neg() (res Int) {
|
||||
return Int{neg(i.i)}
|
||||
}
|
||||
|
||||
func (i Int) String() string {
|
||||
return i.i.String()
|
||||
}
|
||||
|
||||
// MarshalAmino defines custom encoding scheme
|
||||
func (i Int) MarshalAmino() (string, error) {
|
||||
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
|
||||
|
||||
+22
-7
@@ -38,8 +38,8 @@ func NewRat(Numerator int64, Denominator ...int64) Rat {
|
||||
}
|
||||
|
||||
// create a rational from decimal string or integer string
|
||||
func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
|
||||
|
||||
// precision is the number of values after the decimal point which should be read
|
||||
func NewRatFromDecimal(decimalStr string, prec int) (f Rat, err Error) {
|
||||
// first extract any negative symbol
|
||||
neg := false
|
||||
if string(decimalStr[0]) == "-" {
|
||||
@@ -61,6 +61,9 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
|
||||
if len(str[0]) == 0 || len(str[1]) == 0 {
|
||||
return f, ErrUnknownRequest("not a decimal string")
|
||||
}
|
||||
if len(str[1]) > prec {
|
||||
return f, ErrUnknownRequest("string has too many decimals")
|
||||
}
|
||||
numStr = str[0] + str[1]
|
||||
len := int64(len(str[1]))
|
||||
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) {
|
||||
}
|
||||
|
||||
num, errConv := strconv.Atoi(numStr)
|
||||
if errConv != nil {
|
||||
return f, ErrUnknownRequest(errConv.Error())
|
||||
if errConv != nil && strings.HasSuffix(errConv.Error(), "value out of range") {
|
||||
// resort to big int, don't make this default option for efficiency
|
||||
numBig, success := new(big.Int).SetString(numStr, 10)
|
||||
if success != true {
|
||||
return f, ErrUnknownRequest("not a decimal string")
|
||||
}
|
||||
|
||||
if neg {
|
||||
numBig.Neg(numBig)
|
||||
}
|
||||
|
||||
return NewRatFromBigInt(numBig, big.NewInt(denom)), nil
|
||||
} else if errConv != nil {
|
||||
return f, ErrUnknownRequest("not a decimal string")
|
||||
}
|
||||
|
||||
if neg {
|
||||
@@ -105,9 +120,9 @@ func NewRatFromInt(num Int, denom ...Int) Rat {
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
|
||||
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
|
||||
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
|
||||
func (r Rat) Num() Int { return Int{r.Rat.Num()} } // Num - return the numerator
|
||||
func (r Rat) Denom() Int { return Int{r.Rat.Denom()} } // Denom - return the denominator
|
||||
func (r Rat) IsZero() bool { return r.Num().IsZero() } // IsZero - Is the Rat equal to zero
|
||||
func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 }
|
||||
func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than
|
||||
func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal
|
||||
|
||||
+16
-9
@@ -21,6 +21,8 @@ func TestNew(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewFromDecimal(t *testing.T) {
|
||||
largeBigInt, success := new(big.Int).SetString("3109736052979742687701388262607869", 10)
|
||||
require.True(t, success)
|
||||
tests := []struct {
|
||||
decimalStr string
|
||||
expErr bool
|
||||
@@ -31,7 +33,13 @@ func TestNewFromDecimal(t *testing.T) {
|
||||
{"1.1", false, NewRat(11, 10)},
|
||||
{"0.75", false, NewRat(3, 4)},
|
||||
{"0.8", false, NewRat(4, 5)},
|
||||
{"0.11111", false, NewRat(11111, 100000)},
|
||||
{"0.11111", true, NewRat(1111, 10000)},
|
||||
{"628240629832763.5738930323617075341", true, NewRat(3141203149163817869, 5000)},
|
||||
{"621947210595948537540277652521.5738930323617075341",
|
||||
true, NewRatFromBigInt(largeBigInt, big.NewInt(5000))},
|
||||
{"628240629832763.5738", false, NewRat(3141203149163817869, 5000)},
|
||||
{"621947210595948537540277652521.5738",
|
||||
false, NewRatFromBigInt(largeBigInt, big.NewInt(5000))},
|
||||
{".", true, Rat{}},
|
||||
{".0", true, Rat{}},
|
||||
{"1.", true, Rat{}},
|
||||
@@ -41,22 +49,21 @@ func TestNewFromDecimal(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
res, err := NewRatFromDecimal(tc.decimalStr)
|
||||
res, err := NewRatFromDecimal(tc.decimalStr, 4)
|
||||
if tc.expErr {
|
||||
assert.NotNil(t, err, tc.decimalStr)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.Equal(tc.exp))
|
||||
require.Nil(t, err, tc.decimalStr)
|
||||
require.True(t, res.Equal(tc.exp), tc.decimalStr)
|
||||
}
|
||||
|
||||
// negative tc
|
||||
res, err = NewRatFromDecimal("-" + tc.decimalStr)
|
||||
res, err = NewRatFromDecimal("-"+tc.decimalStr, 4)
|
||||
if tc.expErr {
|
||||
assert.NotNil(t, err, tc.decimalStr)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.Equal(tc.exp.Mul(NewRat(-1))))
|
||||
assert.Nil(t, err, tc.decimalStr)
|
||||
assert.True(t, res.Equal(tc.exp.Mul(NewRat(-1))), tc.decimalStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +140,7 @@ func TestArithmetic(t *testing.T) {
|
||||
assert.True(t, tc.resAdd.Equal(tc.r1.Add(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)
|
||||
assert.True(t, tc.resSub.Equal(tc.r1.Sub(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)
|
||||
|
||||
if tc.r2.Num() == 0 { // panic for divide by zero
|
||||
if tc.r2.Num().IsZero() { // panic for divide by zero
|
||||
assert.Panics(t, func() { tc.r1.Quo(tc.r2) })
|
||||
} else {
|
||||
assert.True(t, tc.resDiv.Equal(tc.r1.Quo(tc.r2)), "r1 %v, r2 %v", tc.r1.Rat, tc.r2.Rat)
|
||||
|
||||
Reference in New Issue
Block a user