diff --git a/types/errors.go b/types/errors.go index 1115d39376..48ca8287a0 100644 --- a/types/errors.go +++ b/types/errors.go @@ -109,6 +109,10 @@ func ErrInvalidCoins(msg string) Error { return newError(CodeInvalidCoins, msg) } +func ErrInvalidCoins(coins Coins) Error { + return newError(CodeInvalidCoins, coins.String()) +} + //---------------------------------------- // Error & sdkError diff --git a/types/rational.go b/types/rational.go index 907ff2f319..3fe1a31742 100644 --- a/types/rational.go +++ b/types/rational.go @@ -1,6 +1,9 @@ package types import ( + "bytes" + "encoding/json" + "fmt" "math/big" "strconv" "strings" @@ -13,12 +16,21 @@ import ( // __| |_ |_ // Rat - extend big.Rat +// NOTE: never use new(Rat) or else +// we will panic unmarshalling into the +// nil embedded big.Rat type Rat struct { *big.Rat `json:"rat"` } -// Rational - big Rat with additional functionality -type Rational interface { +type Rational = Rat + +// RationalInterface - big Rat with additional functionality +// NOTE: we only have one implementation of this interface +// and don't use it anywhere, but it might come in handy +// if we want to provide Rational types that include +// the units of the value in the type system. +type RationalInterface interface { GetRat() *big.Rat Num() int64 Denom() int64 @@ -200,6 +212,37 @@ func (r Rat) Round(precisionFactor int64) Rational { //return nil //} -//nolint -func (r Rat) MarshalJSON() ([]byte, error) { return r.MarshalText() } -func (r *Rat) UnmarshalJSON(data []byte) (err error) { return r.UnmarshalText(data) } +var ratCdc JSONCodec // TODO wire.Codec + +// Hack to just use json.Marshal for everything until +// we update for amino +type JSONCodec struct{} + +func (jc JSONCodec) MarshalJSON(o interface{}) ([]byte, error) { + return json.Marshal(o) +} + +func (jc JSONCodec) UnmarshalJSON(bz []byte, o interface{}) error { + return json.Unmarshal(bz, o) +} + +// Wraps r.MarshalText() in quotes to make it a valid JSON string. +func (r Rat) MarshalJSON() ([]byte, error) { + bz, err := r.MarshalText() + if err != nil { + return bz, err + } + return []byte(fmt.Sprintf(`"%s"`, bz)), nil +} + +// Requires a valid JSON string - strings quotes and calls UnmarshalText +func (r *Rat) UnmarshalJSON(data []byte) (err error) { + quote := []byte(`"`) + if len(data) < 2 || + !bytes.HasPrefix(data, quote) || + !bytes.HasSuffix(data, quote) { + return fmt.Errorf("JSON encoded Rat must be a quote-delimitted string") + } + data = bytes.Trim(data, `"`) + return r.UnmarshalText(data) +} diff --git a/types/rational_test.go b/types/rational_test.go index 9376a8e463..3794627789 100644 --- a/types/rational_test.go +++ b/types/rational_test.go @@ -1,7 +1,6 @@ package types import ( - "encoding/json" "math/big" "testing" @@ -193,41 +192,44 @@ func TestRound(t *testing.T) { } func TestZeroSerializationJSON(t *testing.T) { - var r Rat - err := json.Unmarshal([]byte("{\"numerator\":0,\"denominator\":1}"), &r) + r := NewRat(0, 1) + err := r.UnmarshalJSON([]byte(`"0/1"`)) assert.Nil(t, err) - err = json.Unmarshal([]byte("{\"numerator\":0,\"denominator\":0}"), &r) + err = r.UnmarshalJSON([]byte(`"0/0"`)) assert.NotNil(t, err) - err = json.Unmarshal([]byte("{\"numerator\":1,\"denominator\":0}"), &r) + err = r.UnmarshalJSON([]byte(`"1/0"`)) assert.NotNil(t, err) - err = json.Unmarshal([]byte("{}"), &r) + err = r.UnmarshalJSON([]byte(`"{}"`)) assert.NotNil(t, err) } func TestSerializationJSON(t *testing.T) { r := NewRat(1, 3) - rMarshal, err := json.Marshal(r) + bz, err := r.MarshalText() require.Nil(t, err) - var rUnmarshal Rat - err = json.Unmarshal(rMarshal, &rUnmarshal) + r2 := NewRat(0, 1) + err = r2.UnmarshalText(bz) require.Nil(t, err) - assert.True(t, r.Equal(rUnmarshal), "original: %v, unmarshalled: %v", r, rUnmarshal) + assert.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) } func TestSerializationGoWire(t *testing.T) { r := NewRat(1, 3) - rMarshal, err := ratCdc.MarshalJSON(r) + bz, err := ratCdc.MarshalJSON(r) require.Nil(t, err) - var rUnmarshal Rat - err = ratCdc.UnmarshalJSON(rMarshal, &rUnmarshal) + bz, err = r.MarshalJSON() require.Nil(t, err) - assert.True(t, r.Equal(rUnmarshal), "original: %v, unmarshalled: %v", r, rUnmarshal) + r2 := NewRat(0, 1) + err = ratCdc.UnmarshalJSON(bz, &r2) + require.Nil(t, err) + + assert.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) } type testEmbedStruct struct { @@ -237,38 +239,18 @@ type testEmbedStruct struct { } func TestEmbeddedStructSerializationGoWire(t *testing.T) { - r := testEmbedStruct{"foo", 10, NewRat(1, 3)} + obj := testEmbedStruct{"foo", 10, NewRat(1, 3)} - rMarshal, err := ratCdc.MarshalJSON(r) + bz, err := ratCdc.MarshalJSON(obj) require.Nil(t, err) - var rUnmarshal testEmbedStruct - err = ratCdc.UnmarshalJSON(rMarshal, &rUnmarshal) + var obj2 testEmbedStruct + obj2.Field3 = NewRat(0, 1) // ... needs to be initialized + err = ratCdc.UnmarshalJSON(bz, &obj2) require.Nil(t, err) - assert.Equal(t, r.Field1, rUnmarshal.Field1) - assert.Equal(t, r.Field2, rUnmarshal.Field2) - assert.True(t, r.Field3.Equal(rUnmarshal.Field3), "original: %v, unmarshalled: %v", r, rUnmarshal) + assert.Equal(t, obj.Field1, obj2.Field1) + assert.Equal(t, obj.Field2, obj2.Field2) + assert.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2) } - -type testEmbedInterface struct { - Field1 string `json:"f1"` - Field2 int `json:"f2"` - Field3 Rational `json:"f3"` -} - -func TestEmbeddedInterfaceSerializationGoWire(t *testing.T) { - r := testEmbedInterface{"foo", 10, NewRat(1, 3)} - - rMarshal, err := ratCdc.MarshalJSON(r) - require.Nil(t, err) - - var rUnmarshal testEmbedInterface - err = ratCdc.UnmarshalJSON(rMarshal, &rUnmarshal) - require.Nil(t, err) - - assert.Equal(t, r.Field1, rUnmarshal.Field1) - assert.Equal(t, r.Field2, rUnmarshal.Field2) - assert.True(t, r.Field3.Equal(rUnmarshal.Field3), "original: %v, unmarshalled: %v", r, rUnmarshal) -} diff --git a/x/stake/mapper.go b/x/stake/mapper.go index b763a26828..caebe84999 100644 --- a/x/stake/mapper.go +++ b/x/stake/mapper.go @@ -301,7 +301,7 @@ func (m Mapper) removeDelegatorBond(delegator sdk.Address, candidateAddr sdk.Add addrs = append(addrs[:i], addrs[i+1:]...) } } - b, err := m.cdc.MarshalJSON(pks) + b, err := m.cdc.MarshalJSON(addrs) if err != nil { panic(err) } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 997b55102a..e04151046d 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -11,6 +11,7 @@ import ( crypto "github.com/tendermint/go-crypto" dbm "github.com/tendermint/tmlibs/db" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -21,18 +22,18 @@ func subspace(prefix []byte) (start, end []byte) { return prefix, end } -func createTestInput(t *testing.T, isCheckTx bool) (store sdk.KVStore, ctx sdk.Context, key sdk.StoreKey) { +func createTestInput(t *testing.T, isCheckTx bool) (sdk.KVStore, sdk.Context, sdk.StoreKey) { db := dbm.NewMemDB() - key = sdk.NewKVStoreKey("stake") + key := sdk.NewKVStoreKey("stake") ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) err := ms.LoadLatestVersion() require.Nil(t, err) - ctx = sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil) - store = ms.GetKVStore(key) - return + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil) + store := ms.GetKVStore(key) + return store, ctx, key } func newAddrs(n int) (addrs []crypto.Address) { @@ -50,7 +51,7 @@ func newPubKey(pk string) (res crypto.PubKey) { //res, err = crypto.PubKeyFromBytes(pkBytes) var pkEd crypto.PubKeyEd25519 copy(pkEd[:], pkBytes[:]) - return pkEd + return pkEd.Wrap() } // dummy pubkeys used for testing @@ -74,7 +75,7 @@ func candidatesFromActors(store sdk.KVStore, addrs []crypto.Address, amts []int6 c := &Candidate{ Status: Unbonded, PubKey: pks[i], - Owner: addrs[i], + Address: addrs[i], Assets: sdk.NewRat(amts[i]), Liabilities: sdk.NewRat(amts[i]), VotingPower: sdk.NewRat(amts[i]), @@ -83,12 +84,14 @@ func candidatesFromActors(store sdk.KVStore, addrs []crypto.Address, amts []int6 } } +func saveCandidate(store sdk.KVStore, c *Candidate) {} // TODO + func candidatesFromActorsEmpty(addrs []crypto.Address) (candidates Candidates) { for i := 0; i < len(addrs); i++ { c := &Candidate{ Status: Unbonded, PubKey: pks[i], - Owner: addrs[i], + Address: addrs[i], Assets: sdk.ZeroRat, Liabilities: sdk.ZeroRat, VotingPower: sdk.ZeroRat, diff --git a/x/stake/tick.go b/x/stake/tick.go index 3f0d38bcd7..4b206f4dcc 100644 --- a/x/stake/tick.go +++ b/x/stake/tick.go @@ -22,6 +22,7 @@ func Tick(ctx sdk.Context, m Mapper) (change []*abci.Validator, err error) { newVals := m.getValidators(params.MaxVals) // XXX determine change from old validators, set to change + _ = newVals return change, nil } diff --git a/x/stake/tx.go b/x/stake/tx.go index 6cae6746f9..5cc3de6d39 100644 --- a/x/stake/tx.go +++ b/x/stake/tx.go @@ -167,10 +167,10 @@ type MsgUnbond struct { Shares string `json:"shares"` } -func NewMsgUnbond(shares string, address sdk.Address) MsgDelegate { +func NewMsgUnbond(bond sdk.Coin, address sdk.Address) MsgDelegate { return MsgDelegate{ MsgAddr: NewMsgAddr(address), - Shares: shares, + Bond: bond, // Shares: shares, } } @@ -202,10 +202,10 @@ func (msg MsgUnbond) ValidateBasic() sdk.Error { func validateCoin(coin sdk.Coin) sdk.Error { coins := sdk.Coins{coin} if !coins.IsValid() { - return sdk.ErrInvalidCoins() + return sdk.ErrInvalidCoins(coins) } if !coins.IsPositive() { - return fmt.Errorf("Amount must be > 0") + return sdk.ErrInvalidCoins(coins) // XXX: add "Amount must be > 0" ? } return nil } diff --git a/x/stake/types.go b/x/stake/types.go index dd5272e0df..698c7813d8 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -2,7 +2,6 @@ package stake import ( sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" ) @@ -167,7 +166,7 @@ type Description struct { func NewCandidate(pubKey crypto.PubKey, address sdk.Address, description Description) *Candidate { return &Candidate{ Status: Unbonded, - PubKey: pubKet, + PubKey: pubKey, Address: address, Assets: sdk.ZeroRat, Liabilities: sdk.ZeroRat, @@ -222,7 +221,7 @@ func (c *Candidate) removeShares(shares sdk.Rat, gs *GlobalState) (createdCoins // Should only be called when the Candidate qualifies as a validator. func (c *Candidate) validator() Validator { return Validator{ - PubKey: c.PubKey, + Address: c.Address, // XXX !!! VotingPower: c.VotingPower, } } @@ -234,8 +233,9 @@ type Validator struct { } // ABCIValidator - Get the validator from a bond value +/* TODO func (v Validator) ABCIValidator() (*abci.Validator, error) { - pkBytes, err := cdc.MarshalBinary(v.PubKey) + pkBytes, err := wire.MarshalBinary(v.PubKey) if err != nil { return nil, err } @@ -244,6 +244,7 @@ func (v Validator) ABCIValidator() (*abci.Validator, error) { Power: v.VotingPower.Evaluate(), }, nil } +*/ //_________________________________________________________________________ @@ -263,11 +264,11 @@ type DelegatorBond struct { // Perform all the actions required to bond tokens to a delegator bond from their account func (bond *DelegatorBond) BondCoins(candidate *Candidate, tokens sdk.Coin, tr transact) sdk.Error { - _, err := tr.coinKeeper.SubtractCoins(tr.ctx, d.Address, sdk.Coins{tokens}) + _, err := tr.coinKeeper.SubtractCoins(tr.ctx, candidate.Address, sdk.Coins{tokens}) if err != nil { return err } - newShares = candidate.addTokens(tokens.Amount, tr.gs) + newShares := candidate.addTokens(tokens.Amount, tr.gs) bond.Shares = bond.Shares.Add(newShares) return nil } @@ -277,14 +278,14 @@ func (bond *DelegatorBond) UnbondCoins(candidate *Candidate, shares int64, tr tr // subtract bond tokens from delegator bond if bond.Shares.LT(shares) { - return ErrInsufficientFunds() + return sdk.ErrInsufficientFunds("") // TODO } bond.Shares = bond.Shares.Sub(shares) returnAmount := candidate.removeShares(shares, tr.gs) returnCoins := sdk.Coins{{tr.params.BondDenom, returnAmount}} - _, err := tr.coinKeeper.AddCoins(tr.ctx, d.Address, returnCoins) + _, err := tr.coinKeeper.AddCoins(tr.ctx, candidate.Address, returnCoins) if err != nil { return err }