From 398fb947dfa2bb663d8254dab9adf50874ed62b3 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Mon, 27 Feb 2023 13:18:56 -0500 Subject: [PATCH 01/11] delegations --- x/interchainstaking/types/delegation_test.go | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/x/interchainstaking/types/delegation_test.go b/x/interchainstaking/types/delegation_test.go index 05ca4e408..ccb6c412d 100644 --- a/x/interchainstaking/types/delegation_test.go +++ b/x/interchainstaking/types/delegation_test.go @@ -34,11 +34,17 @@ func TestRoundtripDelegationMarshalToUnmarshal(t *testing.T) { // Finally ensure that the 2nd round marshaled bytes equal the original ones. marshalDelBytes2ndRound := types.MustMarshalDelegation(types.ModuleCdc, unmarshaledDel) require.Equal(t, marshaledDelBytes, marshalDelBytes2ndRound, "all the marshaled bytes should be equal!") + + // ensure error is returned for 0 length + _, err := types.UnmarshalDelegation(types.ModuleCdc, []byte{}) + require.Error(t, err) } func TestSetForValoper(t *testing.T) { v1 := utils.GenerateValAddressForTest().String() v2 := utils.GenerateValAddressForTest().String() + v3 := utils.GenerateValAddressForTest().String() + intents := types.ValidatorIntents{ {ValoperAddress: v1, Weight: sdk.NewDecWithPrec(10, 1)}, {ValoperAddress: v2, Weight: sdk.NewDecWithPrec(90, 1)}, @@ -49,4 +55,51 @@ func TestSetForValoper(t *testing.T) { require.Equal(t, sdk.NewDecWithPrec(40, 1), intents.MustGetForValoper(v1).Weight) require.Equal(t, sdk.NewDecWithPrec(60, 1), intents.MustGetForValoper(v2).Weight) + + // check failed return + actual := intents.MustGetForValoper(v3) + require.Equal(t, sdk.ZeroDec(), actual.Weight) +} + +func TestNormalizeValidatorIntentsDeterminism(t *testing.T) { + v1 := utils.GenerateValAddressForTest().String() + v2 := utils.GenerateValAddressForTest().String() + v3 := utils.GenerateValAddressForTest().String() + v4 := utils.GenerateValAddressForTest().String() + + cases := []struct { + name string + intents types.ValidatorIntents + }{ + { + name: "case 1", + intents: types.ValidatorIntents{ + {ValoperAddress: v1, Weight: sdk.NewDecWithPrec(10, 1)}, + {ValoperAddress: v2, Weight: sdk.NewDecWithPrec(90, 1)}, + }, + }, + { + name: "case 2", + intents: types.ValidatorIntents{ + {ValoperAddress: v1, Weight: sdk.NewDecWithPrec(10, 1)}, + {ValoperAddress: v2, Weight: sdk.NewDecWithPrec(90, 1)}, + {ValoperAddress: v3, Weight: sdk.NewDecWithPrec(90, 1)}, + {ValoperAddress: v4, Weight: sdk.NewDecWithPrec(90, 1)}, + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + cached := tc.intents.Normalize() + // verify that sort is deterministic + for i := 1; i < 3; i++ { + normalized := tc.intents.Normalize() + require.Equal(t, cached, normalized) + + } + }) + } + } From 8c381de6111044da3af04b697e158182fdf4847c Mon Sep 17 00:00:00 2001 From: aljo242 Date: Mon, 27 Feb 2023 13:35:31 -0500 Subject: [PATCH 02/11] zones --- x/interchainstaking/types/zones_test.go | 35 ++++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/x/interchainstaking/types/zones_test.go b/x/interchainstaking/types/zones_test.go index c0204ebdb..9e40d6250 100644 --- a/x/interchainstaking/types/zones_test.go +++ b/x/interchainstaking/types/zones_test.go @@ -227,9 +227,11 @@ func TestBase64MemoToIntent(t *testing.T) { zone.Validators = append(zone.Validators, &types.Validator{ValoperAddress: "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", CommissionRate: sdk.MustNewDecFromStr("0.2"), VotingPower: sdk.NewInt(2000), Status: stakingtypes.BondStatusBonded}) testCases := []struct { + name string memo string amount int expectedIntent map[string]sdk.Dec + wantErr bool }{ { memo: "WoS/+Ex92tEcuMBzhukZKMVnXKS8bqaQBJTx9zza4rrxyLiP9fwLijOc", @@ -258,16 +260,35 @@ func TestBase64MemoToIntent(t *testing.T) { "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll": sdk.NewDecWithPrec(5, 1), }, }, + { + name: "empty memo", + memo: "", + amount: 10, + wantErr: true, + }, + { + name: "invalid length", + memo: "ToS/+Ex92tEcuMBzhukZKMVnXKS8NKaQBJTx9zza4rrxyLiP9fwLijOcPK/59acWzdcBME6ub8f0LID97qWECuxJKXmxBM1YBQyWHSAXDiwmMY78K", + amount: 10, + wantErr: true, + }, } for _, tc := range testCases { - out, err := zone.ConvertMemoToOrdinalIntents(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), tc.memo) - require.NoError(t, err) - for _, v := range out { - if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) { - t.Errorf("Got %v expected %v", v.Weight, tc.expectedIntent[v.ValoperAddress]) + t.Run(tc.name, func(t *testing.T) { + out, err := zone.ConvertMemoToOrdinalIntents(sdk.NewCoins(sdk.NewCoin("uatom", sdk.NewInt(int64(tc.amount)))), tc.memo) + if tc.wantErr { + require.Error(t, err) + return } - } + + require.NoError(t, err) + for _, v := range out { + if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) { + t.Errorf("Got %v expected %v", v.Weight, tc.expectedIntent[v.ValoperAddress]) + } + } + }) } } @@ -484,7 +505,7 @@ func intentFromDecSlice(in map[string]sdk.Dec) types.DelegatorIntent { Intents: []*types.ValidatorIntent{}, } for addr, weight := range in { - out.Intents = append(out.Intents, &types.ValidatorIntent{addr, weight}) + out.Intents = append(out.Intents, &types.ValidatorIntent{ValoperAddress: addr, Weight: weight}) } return out } From cb6171e1394005a6cda60918fd75c40f2f879554 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Mon, 27 Feb 2023 14:49:43 -0500 Subject: [PATCH 03/11] refactor --- x/interchainstaking/keeper/receipt.go | 12 +----- x/interchainstaking/types/receipt.go | 11 +++++ x/interchainstaking/types/receipt_test.go | 52 +++++++++++++++++++++++ 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 x/interchainstaking/types/receipt.go create mode 100644 x/interchainstaking/types/receipt_test.go diff --git a/x/interchainstaking/keeper/receipt.go b/x/interchainstaking/keeper/receipt.go index 0502e896e..1c2d4b600 100644 --- a/x/interchainstaking/keeper/receipt.go +++ b/x/interchainstaking/keeper/receipt.go @@ -15,8 +15,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" - abcitypes "github.com/tendermint/tendermint/abci/types" ) const ( @@ -34,7 +34,7 @@ func (k Keeper) HandleReceiptTransaction(ctx sdk.Context, txr *sdk.TxResponse, t for _, event := range txr.Events { if event.Type == transferPort { - attrs := attributesToMap(event.Attributes) + attrs := types.AttributesToMap(event.Attributes) sender := attrs["sender"] amount := attrs["amount"] if attrs["recipient"] == zone.DepositAddress.GetAddress() { // negate case where sender sends to multiple addresses in one tx @@ -101,14 +101,6 @@ func (k Keeper) HandleReceiptTransaction(ctx sdk.Context, txr *sdk.TxResponse, t return nil } -func attributesToMap(attrs []abcitypes.EventAttribute) map[string]string { - out := make(map[string]string) - for _, attr := range attrs { - out[string(attr.Key)] = string(attr.Value) - } - return out -} - func (k *Keeper) MintQAsset(ctx sdk.Context, sender sdk.AccAddress, senderAddress string, zone types.Zone, inCoins sdk.Coins, returnToSender bool) error { if zone.RedemptionRate.IsZero() { return errors.New("zero redemption rate") diff --git a/x/interchainstaking/types/receipt.go b/x/interchainstaking/types/receipt.go new file mode 100644 index 000000000..2ec2b96e7 --- /dev/null +++ b/x/interchainstaking/types/receipt.go @@ -0,0 +1,11 @@ +package types + +import abcitypes "github.com/tendermint/tendermint/abci/types" + +func AttributesToMap(attrs []abcitypes.EventAttribute) map[string]string { + out := make(map[string]string) + for _, attr := range attrs { + out[string(attr.Key)] = string(attr.Value) + } + return out +} diff --git a/x/interchainstaking/types/receipt_test.go b/x/interchainstaking/types/receipt_test.go new file mode 100644 index 000000000..282c1e628 --- /dev/null +++ b/x/interchainstaking/types/receipt_test.go @@ -0,0 +1,52 @@ +package types_test + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + + "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" +) + +func TestAttributesToMap(t *testing.T) { + tests := []struct { + name string + events []abcitypes.EventAttribute + want map[string]string + }{ + { + name: "parse valid", + events: []abcitypes.EventAttribute{ + { + Key: []byte("sender"), + Value: []byte("sender"), + Index: false, + }, + { + Key: []byte("recipient"), + Value: []byte("recipient"), + Index: false, + }, + { + Key: []byte("amount"), + Value: []byte(strconv.Itoa(100)), + Index: false, + }, + }, + want: map[string]string{ + "sender": "sender", + "recipient": "recipient", + "amount": strconv.Itoa(100), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := types.AttributesToMap(tc.events) + require.Equal(t, tc.want, actual) + }) + } +} From 80e7870a33dd80ba10a4a242bd28107689f3b47d Mon Sep 17 00:00:00 2001 From: aljo242 Date: Tue, 28 Feb 2023 08:56:26 -0500 Subject: [PATCH 04/11] add test --- x/interchainstaking/types/keys.go | 2 +- x/interchainstaking/types/keys_test.go | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/x/interchainstaking/types/keys.go b/x/interchainstaking/types/keys.go index 8d29ac234..8ebb51f08 100644 --- a/x/interchainstaking/types/keys.go +++ b/x/interchainstaking/types/keys.go @@ -70,7 +70,7 @@ func ParseStakingDelegationKey(key []byte) (sdk.AccAddress, sdk.ValAddress, erro } delAddrLen := int(key[1]) if len(key) < 2+delAddrLen { - return nil, nil, errors.New("out of bounds reading delegator address") + return nil, nil, errors.New("invalid delegator address length") } delAddr := key[2 : 2+delAddrLen] // use valAddrLen to validate the val address has not been truncated. diff --git a/x/interchainstaking/types/keys_test.go b/x/interchainstaking/types/keys_test.go index 2ae1c34e3..f61372f5e 100644 --- a/x/interchainstaking/types/keys_test.go +++ b/x/interchainstaking/types/keys_test.go @@ -22,6 +22,18 @@ func TestParseStakingDelegationKeyValid(t *testing.T) { require.Equal(t, valAddr, val, "require original and parsed validator addresses match") } +func TestParseStakingDelegationKeyInvalidLength(t *testing.T) { + var key []byte + _, _, err := types.ParseStakingDelegationKey(key) + require.Error(t, err, "invalid length (0)") + require.ErrorContains(t, err, "out of bounds reading byte 0") + + key = []byte{0x31, 0x42} + _, _, err = types.ParseStakingDelegationKey(key) + require.Error(t, err, "invalid length (1)") + require.ErrorContains(t, err, "invalid delegator address length") +} + func TestParseStakingDelegationKeyInvalidPrefix(t *testing.T) { key := []byte{0x42} _, _, err := types.ParseStakingDelegationKey(key) @@ -36,5 +48,5 @@ func TestParseStakingDelegationKeyInvalidTruncated(t *testing.T) { key := stakingtypes.GetDelegationKey(delAddr, valAddr) // truncate the last byte of the key. _, _, err = types.ParseStakingDelegationKey(key[:len(key)-1]) - require.Errorf(t, err, "out of bounds reading validator address") + require.Error(t, err, "out of bounds reading validator address") } From 2f6c83c942b02fec901f3e8ef16388a4fa52cb39 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Tue, 28 Feb 2023 08:56:59 -0500 Subject: [PATCH 05/11] remove duplicate --- x/interchainstaking/types/keys.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/x/interchainstaking/types/keys.go b/x/interchainstaking/types/keys.go index 8ebb51f08..12157b3df 100644 --- a/x/interchainstaking/types/keys.go +++ b/x/interchainstaking/types/keys.go @@ -74,9 +74,6 @@ func ParseStakingDelegationKey(key []byte) (sdk.AccAddress, sdk.ValAddress, erro } delAddr := key[2 : 2+delAddrLen] // use valAddrLen to validate the val address has not been truncated. - if len(key) < 2+delAddrLen { - return nil, nil, errors.New("out of bounds reading delegator address length") - } valAddrLen := int(key[2+delAddrLen]) if len(key) < 3+delAddrLen+valAddrLen { return nil, nil, errors.New("out of bounds reading validator address") From d1239a0cb8a4c4afab63a48c185e5882fa9c0ed9 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Tue, 28 Feb 2023 13:48:10 -0500 Subject: [PATCH 06/11] full coverage --- x/interchainstaking/types/keys_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x/interchainstaking/types/keys_test.go b/x/interchainstaking/types/keys_test.go index f61372f5e..78407b11a 100644 --- a/x/interchainstaking/types/keys_test.go +++ b/x/interchainstaking/types/keys_test.go @@ -25,12 +25,14 @@ func TestParseStakingDelegationKeyValid(t *testing.T) { func TestParseStakingDelegationKeyInvalidLength(t *testing.T) { var key []byte _, _, err := types.ParseStakingDelegationKey(key) - require.Error(t, err, "invalid length (0)") require.ErrorContains(t, err, "out of bounds reading byte 0") + key = []byte{0x31} + _, _, err = types.ParseStakingDelegationKey(key) + require.ErrorContains(t, err, "out of bounds reading delegator address length") + key = []byte{0x31, 0x42} _, _, err = types.ParseStakingDelegationKey(key) - require.Error(t, err, "invalid length (1)") require.ErrorContains(t, err, "invalid delegator address length") } From a046f7b8447b44536454dda803c9076fc34f0291 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Tue, 28 Feb 2023 15:10:25 -0500 Subject: [PATCH 07/11] full coverage --- x/interchainstaking/types/genesis.go | 2 +- x/interchainstaking/types/params.go | 56 +++++++++++------------- x/interchainstaking/types/params_test.go | 18 ++++++++ 3 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 x/interchainstaking/types/params_test.go diff --git a/x/interchainstaking/types/genesis.go b/x/interchainstaking/types/genesis.go index 5fd4ccf58..a3aa26622 100644 --- a/x/interchainstaking/types/genesis.go +++ b/x/interchainstaking/types/genesis.go @@ -14,5 +14,5 @@ func DefaultGenesis() *GenesisState { // failure. func (gs GenesisState) Validate() error { // TODO: validate genesis state. - return validateParams(gs.Params) + return gs.Params.Validate() } diff --git a/x/interchainstaking/types/params.go b/x/interchainstaking/types/params.go index 91be7163b..5131ae385 100644 --- a/x/interchainstaking/types/params.go +++ b/x/interchainstaking/types/params.go @@ -29,7 +29,7 @@ var ( var _ paramtypes.ParamSet = (*Params)(nil) -// unmarshal the current staking params value from store key or panic +// MustUnmarshalParams unmarshals the current staking params value from store key or panic func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params { params, err := UnmarshalParams(cdc, value) if err != nil { @@ -39,7 +39,7 @@ func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params { return params } -// unmarshal the current staking params value from store key +// UnmarshalParams unmarshals the current staking params value from store key func UnmarshalParams(cdc *codec.LegacyAmino, value []byte) (params Params, err error) { if len(value) == 0 { return params, errors.New("unable to unmarshal empty byte slice") @@ -52,31 +52,6 @@ func UnmarshalParams(cdc *codec.LegacyAmino, value []byte) (params Params, err e return } -func validateParams(i interface{}) error { - v, ok := i.(Params) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v.DepositInterval <= 0 { - return fmt.Errorf("deposit interval must be positive: %d", v.DepositInterval) - } - - if v.ValidatorsetInterval <= 0 { - return fmt.Errorf("valset interval must be positive: %d", v.ValidatorsetInterval) - } - - if v.CommissionRate.IsNil() { - return errors.New("commission rate must be non-nil") - } - - if v.CommissionRate.IsNegative() { - return fmt.Errorf("commission rate must be non-negative: %s", v.CommissionRate.String()) - } - - return nil -} - // NewParams creates a new ics Params instance func NewParams( depositInterval uint64, @@ -102,6 +77,23 @@ func DefaultParams() Params { ) } +// Validate validates params. +func (p Params) Validate() error { + if err := validatePositiveInt(p.DepositInterval); err != nil { + return fmt.Errorf("invalid deposit interval: %w", err) + } + + if err := validatePositiveInt(p.ValidatorsetInterval); err != nil { + return fmt.Errorf("invalid valset interval: %w", err) + } + + if err := validateNonNegativeDec(p.CommissionRate); err != nil { + return fmt.Errorf("invalid commission rate: %w", err) + } + + return nil +} + // ParamKeyTable for ics module. func ParamKeyTable() paramtypes.KeyTable { return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) @@ -158,13 +150,17 @@ func validatePositiveInt(i interface{}) error { } func validateNonNegativeDec(i interface{}) error { - intval, ok := i.(sdk.Dec) + dec, ok := i.(sdk.Dec) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } - if intval.IsNegative() { - return fmt.Errorf("invalid (negative) parameter value: %d", intval) + if dec.IsNil() { + return fmt.Errorf("invalid (nil) parameter value") + } + + if dec.IsNegative() { + return fmt.Errorf("invalid (negative) parameter value: %s", dec.String()) } return nil } diff --git a/x/interchainstaking/types/params_test.go b/x/interchainstaking/types/params_test.go new file mode 100644 index 000000000..45047058f --- /dev/null +++ b/x/interchainstaking/types/params_test.go @@ -0,0 +1,18 @@ +package types_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" +) + +func TestValidateParams(t *testing.T) { + require.NoError(t, types.DefaultParams().Validate(), "default") + require.NoError(t, types.NewParams(1, 1, sdk.NewDec(1), true).Validate(), "valid") + require.Error(t, types.NewParams(0, 1, sdk.NewDec(1), true).Validate(), "0 deposit interval") + require.Error(t, types.NewParams(1, 0, sdk.NewDec(1), true).Validate(), "0 valset interval") + require.Error(t, types.NewParams(1, 1, sdk.NewDec(-1), true).Validate(), "negative commission rate") +} From 6da6b93051fcb344a2ce5b0ef4393abe6e1d324a Mon Sep 17 00:00:00 2001 From: aljo242 Date: Tue, 28 Feb 2023 15:16:53 -0500 Subject: [PATCH 08/11] params test --- x/interchainstaking/types/params.go | 4 ++-- x/interchainstaking/types/params_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/interchainstaking/types/params.go b/x/interchainstaking/types/params.go index 5131ae385..1193d0463 100644 --- a/x/interchainstaking/types/params.go +++ b/x/interchainstaking/types/params.go @@ -29,7 +29,7 @@ var ( var _ paramtypes.ParamSet = (*Params)(nil) -// MustUnmarshalParams unmarshals the current staking params value from store key or panic +// MustUnmarshalParams unmarshals the current interchainstaking params value from store key or panic func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params { params, err := UnmarshalParams(cdc, value) if err != nil { @@ -39,7 +39,7 @@ func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params { return params } -// UnmarshalParams unmarshals the current staking params value from store key +// UnmarshalParams unmarshals the current interchainstaking params value from store key func UnmarshalParams(cdc *codec.LegacyAmino, value []byte) (params Params, err error) { if len(value) == 0 { return params, errors.New("unable to unmarshal empty byte slice") diff --git a/x/interchainstaking/types/params_test.go b/x/interchainstaking/types/params_test.go index 45047058f..a14f344f6 100644 --- a/x/interchainstaking/types/params_test.go +++ b/x/interchainstaking/types/params_test.go @@ -1,9 +1,9 @@ package types_test import ( - sdk "github.com/cosmos/cosmos-sdk/types" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" From d5e1d5a3d309c84264f7064f52e6b20f70a71f47 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Wed, 1 Mar 2023 08:22:30 -0500 Subject: [PATCH 09/11] props --- x/interchainstaking/types/proposals.go | 55 +++++++++- x/interchainstaking/types/proposals_test.go | 111 ++++++++++++++++---- 2 files changed, 143 insertions(+), 23 deletions(-) diff --git a/x/interchainstaking/types/proposals.go b/x/interchainstaking/types/proposals.go index 83126e99d..d9e641b5b 100644 --- a/x/interchainstaking/types/proposals.go +++ b/x/interchainstaking/types/proposals.go @@ -19,8 +19,32 @@ var ( _ govv1beta1.Content = &UpdateZoneProposal{} ) -func NewRegisterZoneProposal(title string, description string, connectionID string, baseDenom string, localDenom string, accountPrefix string, returnToSender bool, unbonding bool, deposits bool, liquidityModule bool, decimals int64) *RegisterZoneProposal { - return &RegisterZoneProposal{Title: title, Description: description, ConnectionId: connectionID, BaseDenom: baseDenom, LocalDenom: localDenom, AccountPrefix: accountPrefix, ReturnToSender: returnToSender, UnbondingEnabled: unbonding, DepositsEnabled: deposits, LiquidityModule: liquidityModule, Decimals: decimals} +func NewRegisterZoneProposal( + title string, + description string, + connectionID string, + baseDenom string, + localDenom string, + accountPrefix string, + returnToSender bool, + unbonding bool, + deposits bool, + liquidityModule bool, + decimals int64, +) *RegisterZoneProposal { + return &RegisterZoneProposal{ + Title: title, + Description: description, + ConnectionId: connectionID, + BaseDenom: baseDenom, + LocalDenom: localDenom, + AccountPrefix: accountPrefix, + ReturnToSender: returnToSender, + UnbondingEnabled: unbonding, + DepositsEnabled: deposits, + LiquidityModule: liquidityModule, + Decimals: decimals, + } } func (m RegisterZoneProposal) GetDescription() string { return m.Description } @@ -79,11 +103,32 @@ func (m RegisterZoneProposal) String() string { Deposits Enabled: %t Liquidity Staking Module Enabled: %t Decimals: %d -`, m.Title, m.Description, m.ConnectionId, m.BaseDenom, m.LocalDenom, m.ReturnToSender, m.UnbondingEnabled, m.DepositsEnabled, m.LiquidityModule, m.Decimals) +`, + m.Title, + m.Description, + m.ConnectionId, + m.BaseDenom, + m.LocalDenom, + m.ReturnToSender, + m.UnbondingEnabled, + m.DepositsEnabled, + m.LiquidityModule, + m.Decimals, + ) } -func NewUpdateZoneProposal(title string, description string, chainID string, changes []*UpdateZoneValue) *UpdateZoneProposal { - return &UpdateZoneProposal{Title: title, Description: description, ChainId: chainID, Changes: changes} +func NewUpdateZoneProposal( + title string, + description string, + chainID string, + changes []*UpdateZoneValue, +) *UpdateZoneProposal { + return &UpdateZoneProposal{ + Title: title, + Description: description, + ChainId: chainID, + Changes: changes, + } } func (m UpdateZoneProposal) GetDescription() string { return m.Description } diff --git a/x/interchainstaking/types/proposals_test.go b/x/interchainstaking/types/proposals_test.go index 4c18ae222..0b6eca921 100644 --- a/x/interchainstaking/types/proposals_test.go +++ b/x/interchainstaking/types/proposals_test.go @@ -1,6 +1,7 @@ -package types +package types_test import ( + "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" "strings" "testing" @@ -17,6 +18,7 @@ func TestRegisterZoneProposal_ValidateBasic(t *testing.T) { AccountPrefix string ReturnToSender bool UnbondingEnabled bool + Deposits bool LiquidityModule bool Decimals int64 } @@ -36,11 +38,29 @@ func TestRegisterZoneProposal_ValidateBasic(t *testing.T) { AccountPrefix: "cosmos", ReturnToSender: false, UnbondingEnabled: false, + Deposits: false, LiquidityModule: false, Decimals: 6, }, wantErr: false, }, + { + name: "invalid gov content", + fields: fields{ + Title: "", + Description: "", + ConnectionId: "connection-0", + BaseDenom: "uatom", + LocalDenom: "uqatom", + AccountPrefix: "cosmos", + ReturnToSender: false, + UnbondingEnabled: false, + Deposits: false, + LiquidityModule: false, + Decimals: 6, + }, + wantErr: true, + }, { name: "invalid connection field", fields: fields{ @@ -52,22 +72,24 @@ func TestRegisterZoneProposal_ValidateBasic(t *testing.T) { AccountPrefix: "cosmos", ReturnToSender: false, UnbondingEnabled: false, + Deposits: false, LiquidityModule: false, Decimals: 6, }, wantErr: true, }, { - name: "invalid basdenom field", + name: "invalid basedenom field", fields: fields{ Title: "Enable testzone-1", Description: "onboard testzone-1", - ConnectionId: "test", + ConnectionId: "connection-0", BaseDenom: "0", LocalDenom: "uqatom", AccountPrefix: "cosmos", ReturnToSender: false, UnbondingEnabled: false, + Deposits: false, LiquidityModule: false, Decimals: 6, }, @@ -78,32 +100,85 @@ func TestRegisterZoneProposal_ValidateBasic(t *testing.T) { fields: fields{ Title: "Enable testzone-1", Description: "onboard testzone-1", - ConnectionId: "test", + ConnectionId: "connection-0", BaseDenom: "uatom", LocalDenom: "0", AccountPrefix: "cosmos", ReturnToSender: false, UnbondingEnabled: false, + Deposits: false, + LiquidityModule: false, + Decimals: 6, + }, + wantErr: true, + }, + { + name: "invalid account prefix", + fields: fields{ + Title: "Enable testzone-1", + Description: "onboard testzone-1", + ConnectionId: "connection-0", + BaseDenom: "uatom", + LocalDenom: "uqatom", + AccountPrefix: "a", + ReturnToSender: false, + UnbondingEnabled: false, + Deposits: false, LiquidityModule: false, Decimals: 6, }, wantErr: true, }, + { + name: "liquidity", + fields: fields{ + Title: "Enable testzone-1", + Description: "onboard testzone-1", + ConnectionId: "connection-0", + BaseDenom: "uatom", + LocalDenom: "uqatom", + AccountPrefix: "cosmos", + ReturnToSender: false, + UnbondingEnabled: false, + Deposits: false, + LiquidityModule: true, + Decimals: 6, + }, + wantErr: true, + }, + { + name: "invalid decimals", + fields: fields{ + Title: "Enable testzone-1", + Description: "onboard testzone-1", + ConnectionId: "connection-0", + BaseDenom: "uatom", + LocalDenom: "uqatom", + AccountPrefix: "cosmos", + ReturnToSender: false, + UnbondingEnabled: false, + Deposits: false, + LiquidityModule: false, + Decimals: 0, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - m := RegisterZoneProposal{ - Title: tt.fields.Title, - Description: tt.fields.Description, - ConnectionId: tt.fields.ConnectionId, - BaseDenom: tt.fields.BaseDenom, - LocalDenom: tt.fields.LocalDenom, - AccountPrefix: tt.fields.AccountPrefix, - ReturnToSender: tt.fields.ReturnToSender, - UnbondingEnabled: tt.fields.UnbondingEnabled, - LiquidityModule: tt.fields.LiquidityModule, - Decimals: tt.fields.Decimals, - } + m := types.NewRegisterZoneProposal( + tt.fields.Title, + tt.fields.Description, + tt.fields.ConnectionId, + tt.fields.BaseDenom, + tt.fields.LocalDenom, + tt.fields.AccountPrefix, + tt.fields.ReturnToSender, + tt.fields.UnbondingEnabled, + tt.fields.Deposits, + tt.fields.LiquidityModule, + tt.fields.Decimals, + ) err := m.ValidateBasic() if tt.wantErr { @@ -119,11 +194,11 @@ func TestRegisterZoneProposal_ValidateBasic(t *testing.T) { var sink interface{} func BenchmarkUpdateZoneProposalString(b *testing.B) { - uzp := &UpdateZoneProposal{ + uzp := &types.UpdateZoneProposal{ Title: "Testing right here", Description: "Testing description", ChainId: "quicksilver", - Changes: []*UpdateZoneValue{ + Changes: []*types.UpdateZoneValue{ {Key: "K1", Value: "V1"}, {Key: strings.Repeat("Ks", 100), Value: strings.Repeat("Vs", 128)}, {Key: strings.Repeat("a", 64), Value: strings.Repeat("A", 28)}, From a3faf1241eed2631ecf41da556df791c8a07f0e8 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Wed, 1 Mar 2023 10:15:51 -0500 Subject: [PATCH 10/11] validator --- x/interchainstaking/types/validator.go | 56 ++++++++++----------- x/interchainstaking/types/validator_test.go | 41 +++++++++++---- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/x/interchainstaking/types/validator.go b/x/interchainstaking/types/validator.go index cfd5a5bbd..9c547ed18 100644 --- a/x/interchainstaking/types/validator.go +++ b/x/interchainstaking/types/validator.go @@ -1,32 +1,32 @@ package types import ( - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) // check to see if two validator instances are equal. Used in testing. -func (v Validator) IsEqual(other Validator) bool { - if v.ValoperAddress != other.ValoperAddress { - return false - } - - if !v.CommissionRate.Equal(other.CommissionRate) { - return false - } - - if !v.DelegatorShares.Equal(other.DelegatorShares) { - return false - } - - if !v.VotingPower.Equal(other.VotingPower) { - return false - } - return true -} - -func (v Validator) SharesToTokens(shares sdk.Dec) math.Int { - if v.DelegatorShares.IsZero() { +// func (v Validator) IsEqual(other Validator) bool { +// if v.ValoperAddress != other.ValoperAddress { +// return false +// } +// +// if !v.CommissionRate.Equal(other.CommissionRate) { +// return false +// } +// +// if !v.DelegatorShares.Equal(other.DelegatorShares) { +// return false +// } +// +// if !v.VotingPower.Equal(other.VotingPower) { +// return false +// } +// return true +// } + +func (v Validator) SharesToTokens(shares sdk.Dec) sdkmath.Int { + if v.DelegatorShares.IsNil() || v.DelegatorShares.IsZero() { return sdk.ZeroInt() } @@ -42,23 +42,23 @@ func (di DelegatorIntent) AddOrdinal(multiplier sdk.Dec, intents ValidatorIntent di.Intents = make(ValidatorIntents, 0) } - di = di.Ordinalize(multiplier) + ordinalized := di.Ordinalize(multiplier) OUTER: for _, i := range intents.Sort() { - for jdx, j := range di.SortedIntents() { + for jdx, j := range ordinalized.SortedIntents() { if i.ValoperAddress == j.ValoperAddress { - di.Intents[jdx].Weight = j.Weight.Add(i.Weight) + ordinalized.Intents[jdx].Weight = j.Weight.Add(i.Weight) continue OUTER } } - di.Intents = append(di.Intents, i) + ordinalized.Intents = append(ordinalized.Intents, i) } // we may have appended above, so resort intents. - di.SortedIntents() + ordinalized.SortedIntents() - return di.Normalize() + return ordinalized.Normalize() } func (di DelegatorIntent) IntentForValoper(valoper string) (*ValidatorIntent, bool) { diff --git a/x/interchainstaking/types/validator_test.go b/x/interchainstaking/types/validator_test.go index b3b0b7c17..e3c9e4faf 100644 --- a/x/interchainstaking/types/validator_test.go +++ b/x/interchainstaking/types/validator_test.go @@ -1,11 +1,10 @@ package types_test import ( - "testing" - - "github.com/stretchr/testify/require" - + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "testing" "github.com/ingenuity-build/quicksilver/utils" "github.com/ingenuity-build/quicksilver/x/interchainstaking/types" @@ -19,6 +18,23 @@ var ( v4 = utils.GenerateValAddressForTest().String() ) +func TestSharesToTokens(t *testing.T) { + val := types.Validator{ + ValoperAddress: v1, + DelegatorShares: sdk.NewDecWithPrec(12345, 3), + VotingPower: sdkmath.NewInt(10), + } + require.Equal(t, sdkmath.NewInt(81), val.SharesToTokens(sdk.NewDec(100))) + + nilSharesVal := types.Validator{} + require.Equal(t, sdkmath.ZeroInt(), nilSharesVal.SharesToTokens(sdk.NewDec(100))) + + nolSharesVal := types.Validator{ + DelegatorShares: sdk.ZeroDec(), + } + require.Equal(t, sdkmath.ZeroInt(), nolSharesVal.SharesToTokens(sdk.NewDec(100))) +} + func TestNormalizeIntentWithZeroLength(t *testing.T) { di := types.DelegatorIntent{Delegator: acc1, Intents: []*types.ValidatorIntent{}} di = di.Normalize() @@ -90,17 +106,24 @@ func TestOrdinalizeIntentWithNonEqualIntents(t *testing.T) { } func TestAddOrdinal(t *testing.T) { - di := types.DelegatorIntent{Delegator: utils.GenerateAccAddressForTest().String(), Intents: []*types.ValidatorIntent{ - {ValoperAddress: v1, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, - {ValoperAddress: v2, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, - {ValoperAddress: v3, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, - }} + di := types.DelegatorIntent{ + Delegator: utils.GenerateAccAddressForTest().String(), + Intents: []*types.ValidatorIntent{ + {ValoperAddress: v1, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, + {ValoperAddress: v2, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, + {ValoperAddress: v3, Weight: sdk.OneDec().Quo(sdk.NewDec(3))}, + }, + } newIntents := types.ValidatorIntents{ {ValoperAddress: v1, Weight: sdk.NewDec(1000)}, {ValoperAddress: v2, Weight: sdk.NewDec(2000)}, } + // do nothing for no validator intents + modified := di.AddOrdinal(sdk.NewDec(600), types.ValidatorIntents{}) + require.Equal(t, di, modified) + di = di.AddOrdinal(sdk.NewDec(6000), newIntents) require.Equal(t, 3, len(di.Intents)) From 40bb32a89e4221b20bcb4f09da3a0204dd9cd3d2 Mon Sep 17 00:00:00 2001 From: aljo242 Date: Wed, 1 Mar 2023 11:09:35 -0500 Subject: [PATCH 11/11] zone --- x/interchainstaking/types/zones.go | 2 +- x/interchainstaking/types/zones_test.go | 116 +++++++++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/x/interchainstaking/types/zones.go b/x/interchainstaking/types/zones.go index ab068153e..7ee352356 100644 --- a/x/interchainstaking/types/zones.go +++ b/x/interchainstaking/types/zones.go @@ -203,7 +203,7 @@ func (z *Zone) GetAggregateIntentOrDefault() ValidatorIntents { return filteredIntents } -// defaultAggregateIntents determines the default aggregate intent (for epoch 0) +// DefaultAggregateIntents determines the default aggregate intent (for epoch 0) func (z *Zone) DefaultAggregateIntents() ValidatorIntents { out := make(ValidatorIntents, 0) for _, val := range z.GetValidatorsSorted() { diff --git a/x/interchainstaking/types/zones_test.go b/x/interchainstaking/types/zones_test.go index 9e40d6250..cd384dec5 100644 --- a/x/interchainstaking/types/zones_test.go +++ b/x/interchainstaking/types/zones_test.go @@ -489,7 +489,6 @@ func TestUpdateIntentWithCoins(t *testing.T) { } for _, tc := range testCases { - intent := zone.UpdateIntentWithCoins(intentFromDecSlice(tc.originalIntent), sdk.NewDec(int64(tc.baseAmount)), tc.amount) for _, v := range intent.Intents { if !tc.expectedIntent[v.ValoperAddress].Equal(v.Weight) { @@ -497,6 +496,121 @@ func TestUpdateIntentWithCoins(t *testing.T) { } } } + +} + +func TestZone_GetBondedValidatorAddressesAsSlice(t *testing.T) { + zone := types.Zone{ConnectionId: "connection-0", ChainId: "cosmoshub-4", AccountPrefix: "cosmos", LocalDenom: "uqatom", BaseDenom: "uatom"} + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusUnbonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusUnbonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1z8zjv3lntpwxua0rtpvgrcwl0nm0tltgpgs6l7", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + + // sorted list + expected := []string{ + "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy", + "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll", + "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", + "cosmosvaloper1z8zjv3lntpwxua0rtpvgrcwl0nm0tltgpgs6l7", + } + require.Equal(t, expected, zone.GetBondedValidatorAddressesAsSlice()) +} + +func TestZone_GetAggregateIntentOrDefault(t *testing.T) { + // empty + zone := types.Zone{} + require.Equal(t, types.ValidatorIntents(nil), zone.GetAggregateIntentOrDefault()) + + zone = types.Zone{ConnectionId: "connection-0", ChainId: "cosmoshub-4", AccountPrefix: "cosmos", LocalDenom: "uqatom", BaseDenom: "uatom"} + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusUnbonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusUnbonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(3000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1z8zjv3lntpwxua0rtpvgrcwl0nm0tltgpgs6l7", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + zone.Validators = append(zone.Validators, &types.Validator{ + ValoperAddress: "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", + CommissionRate: sdk.MustNewDecFromStr("0.2"), + VotingPower: sdk.NewInt(2000), + Status: stakingtypes.BondStatusBonded, + }) + + expected := types.ValidatorIntents{ + &types.ValidatorIntent{ + ValoperAddress: "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy", + Weight: sdk.NewDecWithPrec(25, 2), + }, + &types.ValidatorIntent{ + ValoperAddress: "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll", + Weight: sdk.NewDecWithPrec(25, 2), + }, + &types.ValidatorIntent{ + ValoperAddress: "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", + Weight: sdk.NewDecWithPrec(25, 2), + }, + &types.ValidatorIntent{ + ValoperAddress: "cosmosvaloper1z8zjv3lntpwxua0rtpvgrcwl0nm0tltgpgs6l7", + Weight: sdk.NewDecWithPrec(25, 2), + }, + } + actual := zone.GetAggregateIntentOrDefault() + require.Equal(t, expected, actual) } func intentFromDecSlice(in map[string]sdk.Dec) types.DelegatorIntent {