Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Oct 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] \#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
* [x/stake] \#2508 Utilize Tendermint power for validator power key
* [x/stake] \#2531 Remove all inflation logic
* [x/mint] \#2531 Add minting module and inflation logic
Expand Down
64 changes: 35 additions & 29 deletions types/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,29 +248,33 @@ func (d Dec) QuoInt(i Int) Dec {
}

func (d Dec) String() string {
str := d.ToLeftPaddedWithDecimals(Precision)
placement := len(str) - Precision
if placement < 0 {
panic("too few decimal digits")
bz, err := d.Int.MarshalText()
if err != nil {
return ""
}
var bzWDec []byte
inputSize := len(bz)
// TODO: Remove trailing zeros
// case 1, purely decimal
if inputSize <= 10 {
bzWDec = make([]byte, 12)
// 0. prefix
bzWDec[0] = byte('0')
bzWDec[1] = byte('.')
// set relevant digits to 0
for i := 0; i < 10-inputSize; i++ {
bzWDec[i+2] = byte('0')
}
// set last few digits
copy(bzWDec[2+(10-inputSize):], bz)
} else {
// inputSize + 1 to account for the decimal point that is being added
bzWDec = make([]byte, inputSize+1)
copy(bzWDec, bz[:inputSize-10])
bzWDec[inputSize-10] = byte('.')
copy(bzWDec[inputSize-9:], bz[inputSize-10:])
}
return str[:placement] + "." + str[placement:]
}

// TODO panic if negative or if totalDigits < len(initStr)???
// evaluate as an integer and return left padded string
func (d Dec) ToLeftPaddedWithDecimals(totalDigits int8) string {
intStr := d.Int.String()
fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s`
return fmt.Sprintf(fcode, intStr)
}

// TODO panic if negative or if totalDigits < len(initStr)???
// evaluate as an integer and return left padded string
func (d Dec) ToLeftPadded(totalDigits int8) string {
chopped := chopPrecisionAndRoundNonMutative(d.Int)
intStr := chopped.String()
fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s`
return fmt.Sprintf(fcode, intStr)
return string(bzWDec)
}

// ____
Expand Down Expand Up @@ -407,17 +411,13 @@ 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))
return json.Marshal(d.String())
}

// UnmarshalJSON defines custom decoding scheme
Expand All @@ -431,7 +431,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
}

//___________________________________________________________________________________
Expand Down
50 changes: 36 additions & 14 deletions types/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"math/big"
"testing"

"github.com/stretchr/testify/assert"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -228,26 +230,46 @@ func TestTruncate(t *testing.T) {
}
}

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 {
dec Dec
digits int8
exp string
name string
d Dec
want string
wantErr bool // if wantErr = false, will also attempt unmarshaling
}{
{mustNewDecFromStr(t, "33.3"), 8, "00000033"},
{mustNewDecFromStr(t, "50"), 8, "00000050"},
{mustNewDecFromStr(t, "333"), 8, "00000333"},
{mustNewDecFromStr(t, "333"), 12, "000000000333"},
{mustNewDecFromStr(t, "0.3333"), 8, "00000000"},
{"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 tcIndex, tc := range tests {
res := tc.dec.ToLeftPadded(tc.digits)
require.Equal(t, tc.exp, res, "incorrect left padding, tc %d", tcIndex)
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) {
d := Dec{new(big.Int)}
err := cdc.UnmarshalJSON([]byte(`"0"`), &d)
Expand Down