Merge PR #2506: types: Dec.MarshalJSON now marshals as a normal decimal string

This commit is contained in:
Christopher Goes 2018-10-21 04:24:15 +02:00 committed by GitHub
commit cca2f9db43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 45 deletions

View File

@ -81,6 +81,7 @@ BREAKING CHANGES
* [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding * [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding
* [x/stake] \#2500 Block conflicting redelegations until we add an index * [x/stake] \#2500 Block conflicting redelegations until we add an index
* [x/params] Global Paramstore refactored * [x/params] Global Paramstore refactored
* [types] \#2506 sdk.Dec MarshalJSON now marshals as a normal Decimal, with 10 digits of decimal precision
* [x/stake] \#2508 Utilize Tendermint power for validator power key * [x/stake] \#2508 Utilize Tendermint power for validator power key
* [x/stake] \#2531 Remove all inflation logic * [x/stake] \#2531 Remove all inflation logic
* [x/mint] \#2531 Add minting module and inflation logic * [x/mint] \#2531 Add minting module and inflation logic

View File

@ -248,29 +248,33 @@ func (d Dec) QuoInt(i Int) Dec {
} }
func (d Dec) String() string { func (d Dec) String() string {
str := d.ToLeftPaddedWithDecimals(Precision) bz, err := d.Int.MarshalText()
placement := len(str) - Precision if err != nil {
if placement < 0 { return ""
panic("too few decimal digits")
} }
return str[:placement] + "." + str[placement:] var bzWDec []byte
} inputSize := len(bz)
// TODO: Remove trailing zeros
// TODO panic if negative or if totalDigits < len(initStr)??? // case 1, purely decimal
// evaluate as an integer and return left padded string if inputSize <= 10 {
func (d Dec) ToLeftPaddedWithDecimals(totalDigits int8) string { bzWDec = make([]byte, 12)
intStr := d.Int.String() // 0. prefix
fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` bzWDec[0] = byte('0')
return fmt.Sprintf(fcode, intStr) bzWDec[1] = byte('.')
} // set relevant digits to 0
for i := 0; i < 10-inputSize; i++ {
// TODO panic if negative or if totalDigits < len(initStr)??? bzWDec[i+2] = byte('0')
// evaluate as an integer and return left padded string }
func (d Dec) ToLeftPadded(totalDigits int8) string { // set last few digits
chopped := chopPrecisionAndRoundNonMutative(d.Int) copy(bzWDec[2+(10-inputSize):], bz)
intStr := chopped.String() } else {
fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` // inputSize + 1 to account for the decimal point that is being added
return fmt.Sprintf(fcode, intStr) bzWDec = make([]byte, inputSize+1)
copy(bzWDec, bz[:inputSize-10])
bzWDec[inputSize-10] = byte('.')
copy(bzWDec[inputSize-9:], bz[inputSize-10:])
}
return string(bzWDec)
} }
// ____ // ____
@ -407,17 +411,13 @@ func (d *Dec) UnmarshalAmino(text string) (err error) {
return nil return nil
} }
// MarshalJSON defines custom encoding scheme // MarshalJSON marshals the decimal
func (d Dec) MarshalJSON() ([]byte, error) { func (d Dec) MarshalJSON() ([]byte, error) {
if d.Int == nil { if d.Int == nil {
return nilJSON, nil return nilJSON, nil
} }
bz, err := d.Int.MarshalText() return json.Marshal(d.String())
if err != nil {
return nil, err
}
return json.Marshal(string(bz))
} }
// UnmarshalJSON defines custom decoding scheme // UnmarshalJSON defines custom decoding scheme
@ -431,7 +431,13 @@ func (d *Dec) UnmarshalJSON(bz []byte) error {
if err != nil { if err != nil {
return err return err
} }
return d.Int.UnmarshalText([]byte(text)) // TODO: Reuse dec allocation
newDec, err := NewDecFromStr(text)
if err != nil {
return err
}
d.Int = newDec.Int
return nil
} }
//___________________________________________________________________________________ //___________________________________________________________________________________

View File

@ -4,6 +4,8 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -228,26 +230,46 @@ func TestTruncate(t *testing.T) {
} }
} }
func TestToLeftPadded(t *testing.T) { var cdc = codec.New()
tests := []struct {
dec Dec func TestDecMarshalJSON(t *testing.T) {
digits int8 decimal := func(i int64) Dec {
exp string d := NewDec(0)
}{ d.Int = new(big.Int).SetInt64(i)
{mustNewDecFromStr(t, "33.3"), 8, "00000033"}, return d
{mustNewDecFromStr(t, "50"), 8, "00000050"},
{mustNewDecFromStr(t, "333"), 8, "00000333"},
{mustNewDecFromStr(t, "333"), 12, "000000000333"},
{mustNewDecFromStr(t, "0.3333"), 8, "00000000"},
} }
for tcIndex, tc := range tests { tests := []struct {
res := tc.dec.ToLeftPadded(tc.digits) name string
require.Equal(t, tc.exp, res, "incorrect left padding, tc %d", tcIndex) d Dec
want string
wantErr bool // if wantErr = false, will also attempt unmarshaling
}{
{"zero", decimal(0), "\"0.0000000000\"", false},
{"one", decimal(1), "\"0.0000000001\"", false},
{"ten", decimal(10), "\"0.0000000010\"", false},
{"12340", decimal(12340), "\"0.0000012340\"", false},
{"zeroInt", NewDec(0), "\"0.0000000000\"", false},
{"oneInt", NewDec(1), "\"1.0000000000\"", false},
{"tenInt", NewDec(10), "\"10.0000000000\"", false},
{"12340Int", NewDec(12340), "\"12340.0000000000\"", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.d.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("Dec.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
assert.Equal(t, tt.want, string(got), "incorrect marshalled value")
unmarshalledDec := NewDec(0)
unmarshalledDec.UnmarshalJSON(got)
assert.Equal(t, tt.d, unmarshalledDec, "incorrect unmarshalled value")
}
})
} }
} }
var cdc = codec.New()
func TestZeroDeserializationJSON(t *testing.T) { func TestZeroDeserializationJSON(t *testing.T) {
d := Dec{new(big.Int)} d := Dec{new(big.Int)}
err := cdc.UnmarshalJSON([]byte(`"0"`), &d) err := cdc.UnmarshalJSON([]byte(`"0"`), &d)