fix: tx/textual/valuerenderer: reject non-digits in dec + int (#12817)
This commit is contained in:
parent
33517402b2
commit
e39d84e06f
@ -9,15 +9,15 @@ import (
|
||||
)
|
||||
|
||||
var intValues = []protoreflect.Value{
|
||||
protoreflect.ValueOfString("10.00"),
|
||||
protoreflect.ValueOfString("999.00"),
|
||||
protoreflect.ValueOfString("999.9999"),
|
||||
protoreflect.ValueOfString("99999999.9999"),
|
||||
protoreflect.ValueOfString("1000"),
|
||||
protoreflect.ValueOfString("99900"),
|
||||
protoreflect.ValueOfString("9999999"),
|
||||
protoreflect.ValueOfString("999999999999"),
|
||||
protoreflect.ValueOfString("9999999999999999999"),
|
||||
protoreflect.ValueOfString("1000000000000000000000000000000000000000000000000000000.00"),
|
||||
protoreflect.ValueOfString("77777777777.777777777777777777777700"),
|
||||
protoreflect.ValueOfString("-77777777777.777777777777777777777700"),
|
||||
protoreflect.ValueOfString("777777777777777777777777.77777777700"),
|
||||
protoreflect.ValueOfString("100000000000000000000000000000000000000000000000000000000"),
|
||||
protoreflect.ValueOfString("77777777777777777777777777777777700"),
|
||||
protoreflect.ValueOfString("-77777777777777777777777777777777700"),
|
||||
protoreflect.ValueOfString("77777777777777777777777777777777700"),
|
||||
}
|
||||
|
||||
func BenchmarkIntValueRendererFormat(b *testing.B) {
|
||||
@ -37,6 +37,35 @@ func BenchmarkIntValueRendererFormat(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
var decimalValues = []protoreflect.Value{
|
||||
protoreflect.ValueOfString("10.00"),
|
||||
protoreflect.ValueOfString("999.00"),
|
||||
protoreflect.ValueOfString("999.9999"),
|
||||
protoreflect.ValueOfString("99999999.9999"),
|
||||
protoreflect.ValueOfString("9999999999999999999"),
|
||||
protoreflect.ValueOfString("1000000000000000000000000000000000000000000000000000000.00"),
|
||||
protoreflect.ValueOfString("77777777777.777777777777777777777700"),
|
||||
protoreflect.ValueOfString("-77777777777.777777777777777777777700"),
|
||||
protoreflect.ValueOfString("777777777777777777777777.77777777700"),
|
||||
}
|
||||
|
||||
func BenchmarkDecimalValueRendererFormat(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
dvr := new(decValueRenderer)
|
||||
buf := new(bytes.Buffer)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, value := range intValues {
|
||||
if err := dvr.Format(ctx, value, buf); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
var byteValues = []protoreflect.Value{
|
||||
protoreflect.ValueOfBytes(bytes.Repeat([]byte("abc"), 1<<20)),
|
||||
protoreflect.ValueOfBytes([]byte("999.00")),
|
||||
|
||||
@ -34,15 +34,15 @@ func (vr decValueRenderer) Parse(_ context.Context, r io.Reader) (protoreflect.V
|
||||
// object).
|
||||
func formatDecimal(v string) (string, error) {
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) > 2 {
|
||||
return "", fmt.Errorf("invalid decimal: too many points in %s", v)
|
||||
}
|
||||
|
||||
intPart, err := formatInteger(parts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(parts) > 2 {
|
||||
return "", fmt.Errorf("invalid decimal %s", v)
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
return intPart, nil
|
||||
}
|
||||
@ -52,5 +52,11 @@ func formatDecimal(v string) (string, error) {
|
||||
return intPart, nil
|
||||
}
|
||||
|
||||
// Ensure that the decimal part has only digits.
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/12811
|
||||
if !hasOnlyDigits(decPart) {
|
||||
return "", fmt.Errorf("non-digits detected after decimal point in: %q", decPart)
|
||||
}
|
||||
|
||||
return intPart + "." + decPart, nil
|
||||
}
|
||||
|
||||
35
tx/textual/valuerenderer/dec_test.go
Normal file
35
tx/textual/valuerenderer/dec_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package valuerenderer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatDecimalNonDigits(t *testing.T) {
|
||||
badCases := []string{
|
||||
"10.a",
|
||||
"1a.10",
|
||||
"p1a10.",
|
||||
"0.10p",
|
||||
"--10",
|
||||
"12.😎😎",
|
||||
"11111111111133333333333333333333333333333a",
|
||||
"11111111111133333333333333333333333333333 192892",
|
||||
}
|
||||
|
||||
for _, value := range badCases {
|
||||
value := value
|
||||
t.Run(value, func(t *testing.T) {
|
||||
s, err := formatDecimal(value)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if g, w := err.Error(), "non-digits"; !strings.Contains(g, w) {
|
||||
t.Errorf("Error mismatch\nGot: %q\nWant substring: %q", g, w)
|
||||
}
|
||||
if s != "" {
|
||||
t.Fatalf("Got a non-empty string: %q", s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package valuerenderer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
@ -26,6 +27,18 @@ func (vr intValueRenderer) Parse(_ context.Context, r io.Reader) (protoreflect.V
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func hasOnlyDigits(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range s {
|
||||
if r < '0' || r > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// formatInteger formats an integer into a value-rendered string. This function
|
||||
// operates with string manipulation (instead of manipulating the int or sdk.Int
|
||||
// object).
|
||||
@ -37,7 +50,11 @@ func formatInteger(v string) (string, error) {
|
||||
}
|
||||
if len(v) > 1 {
|
||||
v = strings.TrimLeft(v, "0")
|
||||
}
|
||||
|
||||
// Ensure that the string contains only digits at this point.
|
||||
if !hasOnlyDigits(v) {
|
||||
return "", fmt.Errorf("expecting only digits 0-9, but got non-digits in %q", v)
|
||||
}
|
||||
|
||||
startOffset := 3
|
||||
|
||||
35
tx/textual/valuerenderer/int_test.go
Normal file
35
tx/textual/valuerenderer/int_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package valuerenderer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatIntegerNonDigits(t *testing.T) {
|
||||
badCases := []string{
|
||||
"a10",
|
||||
"1a10",
|
||||
"p1a10",
|
||||
"10p",
|
||||
"--10",
|
||||
"😎😎",
|
||||
"11111111111133333333333333333333333333333a",
|
||||
"11111111111133333333333333333333333333333 192892",
|
||||
}
|
||||
|
||||
for _, value := range badCases {
|
||||
value := value
|
||||
t.Run(value, func(t *testing.T) {
|
||||
s, err := formatInteger(value)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if g, w := err.Error(), "but got non-digits in"; !strings.Contains(g, w) {
|
||||
t.Errorf("Error mismatch\nGot: %q\nWant substring: %q", g, w)
|
||||
}
|
||||
if s != "" {
|
||||
t.Fatalf("Got a non-empty string: %q", s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user