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

test: x/interchainstaking/types #324

Merged
merged 13 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
12 changes: 2 additions & 10 deletions x/interchainstaking/keeper/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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")
Expand Down
53 changes: 53 additions & 0 deletions x/interchainstaking/types/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)},
Expand All @@ -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)

}
})
}

}
2 changes: 1 addition & 1 deletion x/interchainstaking/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
5 changes: 1 addition & 4 deletions x/interchainstaking/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,10 @@ 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.
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")
Expand Down
16 changes: 15 additions & 1 deletion x/interchainstaking/types/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ 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.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.ErrorContains(t, err, "invalid delegator address length")
}

func TestParseStakingDelegationKeyInvalidPrefix(t *testing.T) {
key := []byte{0x42}
_, _, err := types.ParseStakingDelegationKey(key)
Expand All @@ -36,5 +50,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")
}
56 changes: 26 additions & 30 deletions x/interchainstaking/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 interchainstaking params value from store key or panic
func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params {
params, err := UnmarshalParams(cdc, value)
if err != nil {
Expand All @@ -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 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")
Expand All @@ -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,
Expand All @@ -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{})
Expand Down Expand Up @@ -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
}
18 changes: 18 additions & 0 deletions x/interchainstaking/types/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package types_test

import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"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")
}
11 changes: 11 additions & 0 deletions x/interchainstaking/types/receipt.go
Original file line number Diff line number Diff line change
@@ -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
}
52 changes: 52 additions & 0 deletions x/interchainstaking/types/receipt_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
35 changes: 28 additions & 7 deletions x/interchainstaking/types/zones_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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])
}
}
})
}
}

Expand Down Expand Up @@ -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
}
Expand Down