diff --git a/Gopkg.lock b/Gopkg.lock index 46cb95893..57f7aaa79 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -145,7 +145,10 @@ [[projects]] name = "github.com/magiconair/properties" - packages = ["."] + packages = [ + ".", + "assert" + ] revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6" version = "v1.7.6" diff --git a/x/stake/errors.go b/x/stake/errors.go index a8038a3d8..bd1992959 100644 --- a/x/stake/errors.go +++ b/x/stake/errors.go @@ -52,10 +52,10 @@ func ErrCandidateEmpty() sdk.Error { return newError(CodeInvalidValidator, "Cannot bond to an empty candidate") } func ErrBadBondingDenom() sdk.Error { - return newError(CodeInvalidValidator, "Invalid coin denomination") + return newError(CodeInvalidBond, "Invalid coin denomination") } func ErrBadBondingAmount() sdk.Error { - return newError(CodeInvalidValidator, "Amount must be > 0") + return newError(CodeInvalidBond, "Amount must be > 0") } func ErrNoBondingAcct() sdk.Error { return newError(CodeInvalidValidator, "No bond account for this (address, validator) pair") diff --git a/x/stake/msg.go b/x/stake/msg.go index 7a75e1b8f..1305e5f01 100644 --- a/x/stake/msg.go +++ b/x/stake/msg.go @@ -9,7 +9,9 @@ import ( ) // name to idetify transaction types -var MsgType = "stake" +const MsgType = "stake" + +const StakingToken = "fermion" //Verify interface at compile time var _, _, _, _ sdk.Msg = &MsgDeclareCandidacy{}, &MsgEditCandidacy{}, &MsgDelegate{}, &MsgUnbond{} @@ -56,8 +58,12 @@ func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error { if msg.CandidateAddr == nil { return ErrCandidateEmpty() } + if msg.Bond.Denom != StakingToken { + return ErrBadBondingDenom() + } if msg.Bond.Amount <= 0 { - return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String()) + return ErrBadBondingAmount() + // return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String()) } empty := Description{} if msg.Description == empty { @@ -152,8 +158,12 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error { if msg.CandidateAddr == nil { return ErrBadCandidateAddr() } + if msg.Bond.Denom != StakingToken { + return ErrBadBondingDenom() + } if msg.Bond.Amount <= 0 { - return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String()) + return ErrBadBondingAmount() + // return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String()) } return nil } @@ -201,10 +211,13 @@ func (msg MsgUnbond) ValidateBasic() sdk.Error { return ErrBadCandidateAddr() } if msg.Shares != "MAX" { - _, err := sdk.NewRatFromDecimal(msg.Shares) + rat, err := sdk.NewRatFromDecimal(msg.Shares) if err != nil { return ErrBadShares() } + if rat.IsZero() || rat.LT(sdk.ZeroRat) { + return ErrBadShares() + } } return nil } diff --git a/x/stake/msg_test.go b/x/stake/msg_test.go index 476995d65..4b46e49b9 100644 --- a/x/stake/msg_test.go +++ b/x/stake/msg_test.go @@ -1,12 +1,21 @@ package stake import ( + "testing" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/magiconair/properties/assert" + crypto "github.com/tendermint/go-crypto" ) var ( - validator = []byte("addressvalidator1") - empty sdk.Address + addr1 = []byte("addr1") + addr2 = []byte("addr2") + addr3 = []byte("addr3") + emptyAddr sdk.Address + + pubkey1 = crypto.GenPrivKeyEd25519().PubKey() + emptyPubkey crypto.PubKey coinPos = sdk.Coin{"fermion", 1000} coinZero = sdk.Coin{"fermion", 0} @@ -16,42 +25,118 @@ var ( coinNegNotAtoms = sdk.Coin{"foo", -10000} ) -// TODO SUNNY make validate basic tests for each msg.. take these commented tests as inspiration -/* -func TestMsgAddrValidateBasic(t *testing.T) { +func TestMsgDeclareCandidacy(t *testing.T) { tests := []struct { - name string - address sdk.Address - wantErr bool + name string + moniker string + identity string + website string + details string + candidateAddr sdk.Address + pubkey crypto.PubKey + bond sdk.Coin + expectPass bool }{ - {"basic good", addrs[0], false}, - {"empty delegator", sdk.Address{}, true}, + {"basic good", "a", "b", "c", "d", addr1, pubkey1, coinPos, true}, + {"partial description", "", "", "c", "", addr1, pubkey1, coinPos, true}, + {"empty description", "", "", "", "", addr1, pubkey1, coinPos, false}, + {"empty address", "a", "b", "c", "d", emptyAddr, pubkey1, coinPos, false}, + {"empty pubkey", "a", "b", "c", "d", addr1, emptyPubkey, coinPos, true}, + {"empty bond", "a", "b", "c", "d", addr1, pubkey1, coinZero, false}, + {"negative bond", "a", "b", "c", "d", addr1, pubkey1, coinNeg, false}, + {"negative bond", "a", "b", "c", "d", addr1, pubkey1, coinNeg, false}, + {"wrong staking token", "a", "b", "c", "d", addr1, pubkey1, coinPosNotAtoms, false}, } for _, tc := range tests { - tx := NewMsgAddr(tc.address) - assert.Equal(t, tc.wantErr, tx.ValidateBasic() != nil, - "test: %v, tx.ValidateBasic: %v", tc.name, tx.ValidateBasic()) + description := Description{ + Moniker: tc.moniker, + Identity: tc.identity, + Website: tc.website, + Details: tc.details, + } + msg := NewMsgDeclareCandidacy(tc.candidateAddr, tc.pubkey, tc.bond, description) + assert.Equal(t, tc.expectPass, msg.ValidateBasic() == nil, + "test: ", tc.name) } } -func TestValidateCoin(t *testing.T) { +func TestMsgEditCandidacy(t *testing.T) { tests := []struct { - name string - coin sdk.Coin - wantErr bool + name string + moniker string + identity string + website string + details string + candidateAddr sdk.Address + expectPass bool }{ - {"basic good", coinPos, false}, - {"zero coin", coinZero, true}, - {"neg coin", coinNeg, true}, + {"basic good", "a", "b", "c", "d", addr1, true}, + {"partial description", "", "", "c", "", addr1, true}, + {"empty description", "", "", "", "", addr1, false}, + {"empty address", "a", "b", "c", "d", emptyAddr, false}, } for _, tc := range tests { - assert.Equal(t, tc.wantErr, validateCoin(tc.coin) != nil, - "test: %v, tx.ValidateBasic: %v", tc.name, validateCoin(tc.coin)) + description := Description{ + Moniker: tc.moniker, + Identity: tc.identity, + Website: tc.website, + Details: tc.details, + } + msg := NewMsgEditCandidacy(tc.candidateAddr, description) + assert.Equal(t, tc.expectPass, msg.ValidateBasic() == nil, + "test: ", tc.name) + } +} + +func TestMsgDelegate(t *testing.T) { + tests := []struct { + name string + delegatorAddr sdk.Address + candidateAddr sdk.Address + bond sdk.Coin + expectPass bool + }{ + {"basic good", addr1, addr2, coinPos, true}, + {"self bond", addr1, addr1, coinPos, true}, + {"empty delegator", emptyAddr, addr1, coinPos, false}, + {"empty candidate", addr1, emptyAddr, coinPos, false}, + {"empty bond", addr1, addr2, coinZero, false}, + {"negative bond", addr1, addr2, coinNeg, false}, + {"wrong staking token", addr1, addr2, coinPosNotAtoms, false}, + } + + for _, tc := range tests { + msg := NewMsgDelegate(tc.delegatorAddr, tc.candidateAddr, tc.bond) + assert.Equal(t, tc.expectPass, msg.ValidateBasic() == nil, + "test: ", tc.name) + } +} + +func TestMsgUnbond(t *testing.T) { + tests := []struct { + name string + delegatorAddr sdk.Address + candidateAddr sdk.Address + shares string + expectPass bool + }{ + {"max unbond", addr1, addr2, "MAX", true}, + {"decimal unbond", addr1, addr2, "0.1", true}, + {"negative decimal unbond", addr1, addr2, "-0.1", false}, + {"zero unbond", addr1, addr2, "0.0", false}, + {"invalid decimal", addr1, addr1, "sunny", false}, + {"empty delegator", emptyAddr, addr1, "0.1", false}, + {"empty candidate", addr1, emptyAddr, "0.1", false}, + } + + for _, tc := range tests { + msg := NewMsgUnbond(tc.delegatorAddr, tc.candidateAddr, tc.shares) + assert.Equal(t, tc.expectPass, msg.ValidateBasic() == nil, + "test: ", tc.name) } } -*/ // TODO introduce with go-amino //func TestSerializeMsg(t *testing.T) {