From 20cc13e5bf271010fdc641cf622dd1474134c169 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Tue, 16 Oct 2018 09:29:32 -0700 Subject: [PATCH] types: Dec.MarshalJSON now marshals as a normal decimal string Closes #2475 --- PENDING.md | 2 ++ types/decimal.go | 33 +++++++++++++++++++++++++++++---- types/decimal_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/PENDING.md b/PENDING.md index eb61a5468..2f0f9c2d7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -72,6 +72,8 @@ BREAKING CHANGES * [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/params] Global Paramstore refactored + * [types] \#2506 sdk.Dec MarshalJSON now marshals as a normal Decimal, with 10 digits of decimal precision + * Tendermint * Update tendermint version from v0.23.0 to v0.25.0, notable changes diff --git a/types/decimal.go b/types/decimal.go index e9623995f..a3cd7cc0e 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -407,17 +407,36 @@ func (d *Dec) UnmarshalAmino(text string) (err error) { return nil } -// MarshalJSON defines custom encoding scheme +// MarshalJSON marshals the decimal func (d Dec) MarshalJSON() ([]byte, error) { if d.Int == nil { return nilJSON, nil } - bz, err := d.Int.MarshalText() if err != nil { return nil, err } - return json.Marshal(string(bz)) + var bzWDec []byte + // TODO: Remove trailing zeros + // case 1, pure decimal + if len(bz) <= 10 { + bzWDec = make([]byte, 12) + // 0. prefix + bzWDec[0] = byte('0') + bzWDec[1] = byte('.') + // set relevant digits to 0 + for i := 0; i < 10-len(bz); i++ { + bzWDec[i+2] = byte('0') + } + // set last few digits + copy(bzWDec[2+(10-len(bz)):], bz) + } else { + bzWDec = make([]byte, len(bz)+1) + copy(bzWDec, bz[:len(bz)-10]) + bzWDec[len(bz)-10] = byte('.') + copy(bzWDec[len(bz)-9:], bz[len(bz)-10:]) + } + return json.Marshal(string(bzWDec)) } // UnmarshalJSON defines custom decoding scheme @@ -431,7 +450,13 @@ func (d *Dec) UnmarshalJSON(bz []byte) error { if err != nil { 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 } //___________________________________________________________________________________ diff --git a/types/decimal_test.go b/types/decimal_test.go index a6ec0740e..73a00f60a 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -4,6 +4,8 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/assert" + "github.com/cosmos/cosmos-sdk/codec" "github.com/stretchr/testify/require" ) @@ -248,6 +250,44 @@ func TestToLeftPadded(t *testing.T) { var cdc = codec.New() +func TestDecMarshalJSON(t *testing.T) { + decimal := func(i int64) Dec { + d := NewDec(0) + d.Int = new(big.Int).SetInt64(i) + return d + } + tests := []struct { + name string + 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") + } + }) + } +} + func TestZeroDeserializationJSON(t *testing.T) { d := Dec{new(big.Int)} err := cdc.UnmarshalJSON([]byte(`"0"`), &d)