From 6f9122cf4afa80fe1f295825e5593652b2a2dd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E5=BC=BA?= Date: Tue, 26 Jan 2021 18:35:38 +0800 Subject: [PATCH 1/3] Optimized token accuracy conversion --- modules/token/types/token.go | 30 +++---- modules/token/types/token_test.go | 135 ++++++++++++++++++++++++++---- 2 files changed, 129 insertions(+), 36 deletions(-) diff --git a/modules/token/types/token.go b/modules/token/types/token.go index 41f1243c..720b2e45 100644 --- a/modules/token/types/token.go +++ b/modules/token/types/token.go @@ -2,7 +2,7 @@ package types import ( "encoding/json" - "math" + "math/big" "strconv" "github.com/gogo/protobuf/proto" @@ -12,6 +12,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) +var ( + _ proto.Message = &Token{} + tenInt = big.NewInt(10) +) + +// TokenI define a interface for Token type TokenI interface { GetSymbol() string GetName() string @@ -26,8 +32,6 @@ type TokenI interface { ToMinCoin(coin sdk.DecCoin) (sdk.Coin, error) } -var _ proto.Message = &Token{} - // NewToken constructs a new Token instance func NewToken( symbol string, @@ -115,15 +119,9 @@ func (t Token) ToMainCoin(coin sdk.Coin) (sdk.DecCoin, error) { return sdk.NewDecCoin(coin.Denom, coin.Amount), nil } - precision := math.Pow10(int(t.Scale)) - precisionStr := strconv.FormatFloat(precision, 'f', 0, 64) - precisionDec, err := sdk.NewDecFromStr(precisionStr) - if err != nil { - return sdk.DecCoin{}, err - } - + precision := new(big.Int).Exp(tenInt, big.NewInt(int64(t.Scale)), nil) // dest amount = src amount / 10^(scale) - amount := sdk.NewDecFromInt(coin.Amount).Quo(precisionDec) + amount := sdk.NewDecFromInt(coin.Amount).Quo(sdk.NewDecFromBigInt(precision)) return sdk.NewDecCoinFromDec(t.Symbol, amount), nil } @@ -137,15 +135,9 @@ func (t Token) ToMinCoin(coin sdk.DecCoin) (newCoin sdk.Coin, err error) { return sdk.NewCoin(coin.Denom, coin.Amount.TruncateInt()), nil } - precision := math.Pow10(int(t.Scale)) - precisionStr := strconv.FormatFloat(precision, 'f', 0, 64) - precisionDec, err := sdk.NewDecFromStr(precisionStr) - if err != nil { - return sdk.Coin{}, err - } - + precision := new(big.Int).Exp(tenInt, big.NewInt(int64(t.Scale)), nil) // dest amount = src amount * 10^(dest scale) - amount := coin.Amount.Mul(precisionDec) + amount := coin.Amount.Mul(sdk.NewDecFromBigInt(precision)) return sdk.NewCoin(t.MinUnit, amount.TruncateInt()), nil } diff --git a/modules/token/types/token_test.go b/modules/token/types/token_test.go index cb46c7ed..67c40176 100644 --- a/modules/token/types/token_test.go +++ b/modules/token/types/token_test.go @@ -1,15 +1,15 @@ package types import ( + "fmt" + "reflect" "testing" - "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestToken_ToMinCoin(t *testing.T) { - token := Token{ +var ( + token = Token{ Symbol: "iris", Name: "irisnet", Scale: 18, @@ -19,19 +19,7 @@ func TestToken_ToMinCoin(t *testing.T) { Mintable: true, Owner: "", } - - amt, err := sdk.NewDecFromStr("1.500000000000000001") - require.NoError(t, err) - coin := sdk.NewDecCoinFromDec(token.Symbol, amt) - - c, err := token.ToMinCoin(coin) - require.NoError(t, err) - require.Equal(t, "1500000000000000001atto", c.String()) - - coin1, err := token.ToMainCoin(c) - require.NoError(t, err) - require.Equal(t, coin, coin1) -} +) func TestCheckKeywords(t *testing.T) { type args struct { @@ -61,3 +49,116 @@ func TestCheckKeywords(t *testing.T) { }) } } + +func TestToken_ToMinCoin(t *testing.T) { + type args struct { + coin sdk.DecCoin + } + + for i := uint32(0); i <= MaximumScale; i++ { + token.Scale = i + tests := []struct { + name string + args args + want sdk.Coin + wantErr bool + success bool + }{ + { + name: fmt.Sprintf("Main Coin to Min Coin,scale=%d", i), + wantErr: false, + args: args{coin: sdk.NewDecCoin(token.Symbol, sdk.NewInt(10))}, + want: sdk.NewCoin(token.MinUnit, sdk.NewIntWithDecimal(10, int(token.Scale))), + success: true, + }, + { + name: fmt.Sprintf("Main Coin to Min Coin Failed,scale=%d", i), + wantErr: false, + args: args{coin: sdk.NewDecCoin(token.Symbol, sdk.NewInt(10))}, + want: sdk.NewCoin(token.MinUnit, sdk.NewInt(10)), + success: (i == 0), + }, + { + name: fmt.Sprintf("Min Coin to Min Coin Success,scale=%d", i), + wantErr: false, + args: args{coin: sdk.NewDecCoin(token.MinUnit, sdk.NewInt(10))}, + want: sdk.NewCoin(token.MinUnit, sdk.NewInt(10)), + success: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Token{ + Symbol: token.Symbol, + Scale: token.Scale, + MinUnit: token.MinUnit, + } + got, err := tr.ToMinCoin(tt.args.coin) + if (err != nil) != tt.wantErr { + t.Errorf("Token.ToMainCoin() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.success != reflect.DeepEqual(got, tt.want) { + t.Errorf("Token.ToMainCoin() = %v, want %v", got, tt.want) + } + }) + } + } +} + +func TestToken_ToMainCoin(t *testing.T) { + type args struct { + coin sdk.Coin + } + + for i := uint32(0); i <= MaximumScale; i++ { + token.Scale = i + tests := []struct { + name string + args args + want sdk.DecCoin + wantErr bool + success bool + }{ + { + name: "Main Coin to Main Coin", + wantErr: false, + args: args{coin: sdk.NewCoin(token.Symbol, sdk.NewInt(10))}, + want: sdk.NewInt64DecCoin(token.Symbol, 10), + success: true, + }, + { + name: "Min Coin to Main Coin Failed", + wantErr: false, + args: args{coin: sdk.NewCoin(token.MinUnit, sdk.NewInt(10))}, + want: sdk.NewInt64DecCoin(token.Symbol, 10), + success: (i == 0), + }, + { + name: "Min Coin to Main Coin Success", + wantErr: false, + args: args{coin: sdk.NewCoin(token.MinUnit, sdk.NewInt(10))}, + want: sdk.NewDecCoinFromDec(token.Symbol, + sdk.NewDecWithPrec(1, int64(token.Scale)).MulInt64(10)), + success: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := Token{ + Symbol: token.Symbol, + Scale: token.Scale, + MinUnit: token.MinUnit, + } + got, err := tr.ToMainCoin(tt.args.coin) + if (err != nil) != tt.wantErr { + t.Errorf("Token.ToMainCoin() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.success != reflect.DeepEqual(got, tt.want) { + t.Errorf("Token.ToMainCoin() = %v, want %v", got, tt.want) + } + }) + } + } +} From cd09404b014ce2b4a206cc4ab857f97d0ccb6d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E5=BC=BA?= Date: Tue, 2 Feb 2021 10:05:37 +0800 Subject: [PATCH 2/3] adjust data validation --- modules/oracle/types/genesis.go | 3 -- modules/oracle/types/msgs.go | 4 -- modules/oracle/types/validation.go | 26 +++++-------- modules/oracle/types/validation_test.go | 52 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 modules/oracle/types/validation_test.go diff --git a/modules/oracle/types/genesis.go b/modules/oracle/types/genesis.go index aa996691..bb89bee0 100644 --- a/modules/oracle/types/genesis.go +++ b/modules/oracle/types/genesis.go @@ -21,9 +21,6 @@ func ValidateGenesis(data GenesisState) error { if err := ValidateAggregateFunc(feed.AggregateFunc); err != nil { return err } - if err := ValidateValueJSONPath(feed.ValueJsonPath); err != nil { - return err - } if err := ValidateLatestHistory(feed.LatestHistory); err != nil { return err } diff --git a/modules/oracle/types/msgs.go b/modules/oracle/types/msgs.go index b73e724a..7b39bb78 100644 --- a/modules/oracle/types/msgs.go +++ b/modules/oracle/types/msgs.go @@ -62,10 +62,6 @@ func (msg MsgCreateFeed) ValidateBasic() error { return err } - if err := ValidateValueJSONPath(msg.ValueJsonPath); err != nil { - return err - } - if !msg.ServiceFeeCap.IsValid() { return sdkerrors.Wrapf(ErrInvalidServiceFeeCap, msg.ServiceFeeCap.String()) } diff --git a/modules/oracle/types/validation.go b/modules/oracle/types/validation.go index 134735f0..7763bb60 100644 --- a/modules/oracle/types/validation.go +++ b/modules/oracle/types/validation.go @@ -10,15 +10,17 @@ import ( ) const ( - MaxLatestHistory = 100 - MaxAggregateFuncLen = 10 - MaxValueJsonPath = 70 - MaxDescriptionLen = 280 + //MaxLatestHistory define the the maximum number of feed value saved + MaxLatestHistory = 100 + //MaxAggregateFunNmLen define the the maximum lenght of the ggregate function name + MaxAggregateFunNmLen = 10 + //MaxDescriptionLen define the the maximum lenght of the description + MaxDescriptionLen = 280 ) var ( - // the feed name only accepts alphanumeric characters, _ and - - regexpFeedName = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`) + // the feed name only accepts alphanumeric characters, _ and - / + regexpFeedName = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9/_-]*$`) ) // ValidateFeedName verify that the feedName is legal @@ -39,8 +41,8 @@ func ValidateDescription(desc string) error { // ValidateAggregateFunc verify that the aggregateFunc is legal func ValidateAggregateFunc(aggregateFunc string) error { - if len(aggregateFunc) == 0 || len(aggregateFunc) > MaxAggregateFuncLen { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "aggregate func must between [1, %d], got: %d", MaxAggregateFuncLen, len(aggregateFunc)) + if len(aggregateFunc) == 0 || len(aggregateFunc) > MaxAggregateFunNmLen { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "aggregate func must between [1, %d], got: %d", MaxAggregateFunNmLen, len(aggregateFunc)) } if _, err := GetAggregateFunc(aggregateFunc); err != nil { @@ -49,14 +51,6 @@ func ValidateAggregateFunc(aggregateFunc string) error { return nil } -// ValidateValueJSONPath verify that the valueJsonPath is legal -func ValidateValueJSONPath(valueJSONPath string) error { - if len(valueJSONPath) == 0 || len(valueJSONPath) > MaxValueJsonPath { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "the length of valueJson path func must less than %d, got: %d", MaxAggregateFuncLen, len(valueJSONPath)) - } - return nil -} - // ValidateLatestHistory verify that the latestHistory is legal func ValidateLatestHistory(latestHistory uint64) error { if latestHistory < 1 || latestHistory > MaxLatestHistory { diff --git a/modules/oracle/types/validation_test.go b/modules/oracle/types/validation_test.go new file mode 100644 index 00000000..b8c7f171 --- /dev/null +++ b/modules/oracle/types/validation_test.go @@ -0,0 +1,52 @@ +package types + +import "testing" + +func TestValidateFeedName(t *testing.T) { + type args struct { + feedName string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "contain -", + args: args{feedName: "usdt-atom"}, + wantErr: false, + }, + { + name: "contain /", + args: args{feedName: "usdt/atom"}, + wantErr: false, + }, + { + name: "contain uppercase letter", + args: args{feedName: "USDT-atom"}, + wantErr: false, + }, + { + name: "contain digital", + args: args{feedName: "USDT-atom2"}, + wantErr: false, + }, + { + name: "start with a number", + args: args{feedName: "2USDT-atom2"}, + wantErr: true, + }, + { + name: "contain special characters", + args: args{feedName: "USDT$-atom2"}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateFeedName(tt.args.feedName); (err != nil) != tt.wantErr { + t.Errorf("ValidateFeedName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From df94ff7e4bfdc6c8bd1d85bd76542383168525ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E5=BC=BA?= Date: Tue, 2 Feb 2021 10:18:07 +0800 Subject: [PATCH 3/3] fix test error --- modules/oracle/types/msgs_test.go | 18 ------------------ modules/oracle/types/validation.go | 8 ++++---- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/modules/oracle/types/msgs_test.go b/modules/oracle/types/msgs_test.go index 5d51eef7..3cd1bbf7 100644 --- a/modules/oracle/types/msgs_test.go +++ b/modules/oracle/types/msgs_test.go @@ -75,24 +75,6 @@ func TestMsgCreateFeed_ValidateBasic(t *testing.T) { Creator: addr1, }, false, - }, { - "wrong ValueJsonPath", - MsgCreateFeed{ - FeedName: "feedEthPrice", - AggregateFunc: "avg", - ValueJsonPath: "", - LatestHistory: 10, - Description: "feed eth price", - ServiceName: "GetEthPrice", - Providers: []string{addr1, addr2}, - Input: "eth", - Timeout: 5, - ServiceFeeCap: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), - RepeatedFrequency: 5, - ResponseThreshold: 1, - Creator: addr1, - }, - false, }, { "wrong MaxLatestHistory(=0)", MsgCreateFeed{ diff --git a/modules/oracle/types/validation.go b/modules/oracle/types/validation.go index 7763bb60..93597321 100644 --- a/modules/oracle/types/validation.go +++ b/modules/oracle/types/validation.go @@ -12,8 +12,8 @@ import ( const ( //MaxLatestHistory define the the maximum number of feed value saved MaxLatestHistory = 100 - //MaxAggregateFunNmLen define the the maximum lenght of the ggregate function name - MaxAggregateFunNmLen = 10 + //MaxAggregateFuncNameLen define the the maximum lenght of the ggregate function name + MaxAggregateFuncNameLen = 10 //MaxDescriptionLen define the the maximum lenght of the description MaxDescriptionLen = 280 ) @@ -41,8 +41,8 @@ func ValidateDescription(desc string) error { // ValidateAggregateFunc verify that the aggregateFunc is legal func ValidateAggregateFunc(aggregateFunc string) error { - if len(aggregateFunc) == 0 || len(aggregateFunc) > MaxAggregateFunNmLen { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "aggregate func must between [1, %d], got: %d", MaxAggregateFunNmLen, len(aggregateFunc)) + if len(aggregateFunc) == 0 || len(aggregateFunc) > MaxAggregateFuncNameLen { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "aggregate func must between [1, %d], got: %d", MaxAggregateFuncNameLen, len(aggregateFunc)) } if _, err := GetAggregateFunc(aggregateFunc); err != nil {