refactor, staking uses addresses instead of pubkey

staking refactoring working

working
This commit is contained in:
rigelrozanski 2018-03-14 19:42:50 +01:00
parent 292e156872
commit dc8636390c
12 changed files with 451 additions and 383 deletions

View File

@ -50,7 +50,7 @@ type Params struct {
ReserveTax rational.Rational // Tax collected on all fees ReserveTax rational.Rational // Tax collected on all fees
MaxVals uint16 // maximum number of validators MaxVals uint16 // maximum number of validators
AllowedBondDenom string // bondable coin denomination BondDenom string // bondable coin denomination
// gas costs for txs // gas costs for txs
GasDeclareCandidacy int64 GasDeclareCandidacy int64

View File

@ -34,6 +34,15 @@ func (msg SetTrendMsg) String() string {
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool) return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
} }
// Get the bytes for the message signer to sign on
func (msg SetTrendMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
// Validate Basic is used to quickly disqualify obviously invalid messages quickly // Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg SetTrendMsg) ValidateBasic() sdk.Error { func (msg SetTrendMsg) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 { if len(msg.Sender) == 0 {
@ -48,15 +57,6 @@ func (msg SetTrendMsg) ValidateBasic() sdk.Error {
return nil return nil
} }
// Get the bytes for the message signer to sign on
func (msg SetTrendMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
//_______________________________________________________________________ //_______________________________________________________________________
// A message type to quiz how cool you are. these fields are can be entirely // A message type to quiz how cool you are. these fields are can be entirely

View File

@ -1,7 +1,6 @@
package types package types
import ( import (
"errors"
"math/big" "math/big"
"strconv" "strconv"
"strings" "strings"
@ -57,7 +56,7 @@ func NewRat(Numerator int64, Denominator ...int64) Rat {
} }
//NewFromDecimal - create a rational from decimal string or integer string //NewFromDecimal - create a rational from decimal string or integer string
func NewRatFromDecimal(decimalStr string) (f Rat, err error) { func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
// first extract any negative symbol // first extract any negative symbol
neg := false neg := false
@ -73,23 +72,23 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err error) {
switch len(str) { switch len(str) {
case 1: case 1:
if len(str[0]) == 0 { if len(str[0]) == 0 {
return f, errors.New("not a decimal string") return f, NewError(CodeUnknownRequest, "not a decimal string")
} }
numStr = str[0] numStr = str[0]
case 2: case 2:
if len(str[0]) == 0 || len(str[1]) == 0 { if len(str[0]) == 0 || len(str[1]) == 0 {
return f, errors.New("not a decimal string") return f, NewError(CodeUnknownRequest, "not a decimal string")
} }
numStr = str[0] + str[1] numStr = str[0] + str[1]
len := int64(len(str[1])) len := int64(len(str[1]))
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64() denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
default: default:
return f, errors.New("not a decimal string") return f, NewError(CodeUnknownRequest, "not a decimal string")
} }
num, err := strconv.Atoi(numStr) num, errConv := strconv.Atoi(numStr)
if err != nil { if errConv != nil {
return f, err return f, NewError(CodeUnknownRequest, errConv.Error())
} }
if neg { if neg {

View File

@ -147,6 +147,7 @@ type StdSignMsg struct {
// XXX: Alt // XXX: Alt
} }
// get message bytes
func (msg StdSignMsg) Bytes() []byte { func (msg StdSignMsg) Bytes() []byte {
return StdSignBytes(msg.ChainID, msg.Sequences, msg.Fee, msg.Msg) return StdSignBytes(msg.ChainID, msg.Sequences, msg.Fee, msg.Msg)
} }

View File

@ -107,7 +107,7 @@ func cmdDeclareCandidacy(cmd *cobra.Command, args []string) error {
Details: viper.GetString(FlagDetails), Details: viper.GetString(FlagDetails),
} }
tx := stake.NewTxDeclareCandidacy(amount, pk, description) tx := stake.NewMsgDeclareCandidacy(amount, pk, description)
return doTx(tx) return doTx(tx)
} }
@ -125,7 +125,7 @@ func cmdEditCandidacy(cmd *cobra.Command, args []string) error {
Details: viper.GetString(FlagDetails), Details: viper.GetString(FlagDetails),
} }
tx := stake.NewTxEditCandidacy(pk, description) tx := stake.NewMsgEditCandidacy(pk, description)
return doTx(tx) return doTx(tx)
} }
@ -140,7 +140,7 @@ func cmdDelegate(cmd *cobra.Command, args []string) error {
return err return err
} }
tx := stake.NewTxDelegate(amount, pk) tx := stake.NewMsgDelegate(amount, pk)
return doTx(tx) return doTx(tx)
} }
@ -167,7 +167,7 @@ func cmdUnbond(cmd *cobra.Command, args []string) error {
return err return err
} }
tx := stake.NewTxUnbond(sharesStr, pk) tx := stake.NewMsgUnbond(sharesStr, pk)
return doTx(tx) return doTx(tx)
} }

View File

@ -43,46 +43,49 @@ func codeToDefaultMsg(code CodeType) string {
//---------------------------------------- //----------------------------------------
// Error constructors // Error constructors
func ErrCandidateEmpty() error { func ErrCandidateEmpty() sdk.Error {
return newError(CodeInvalidValidator, "Cannot bond to an empty candidate") return newError(CodeInvalidValidator, "Cannot bond to an empty candidate")
} }
func ErrBadBondingDenom() error { func ErrBadBondingDenom() sdk.Error {
return newError(CodeInvalidValidator, "Invalid coin denomination") return newError(CodeInvalidValidator, "Invalid coin denomination")
} }
func ErrBadBondingAmount() error { func ErrBadBondingAmount() sdk.Error {
return newError(CodeInvalidValidator, "Amount must be > 0") return newError(CodeInvalidValidator, "Amount must be > 0")
} }
func ErrNoBondingAcct() error { func ErrNoBondingAcct() sdk.Error {
return newError(CodeInvalidValidator, "No bond account for this (address, validator) pair") return newError(CodeInvalidValidator, "No bond account for this (address, validator) pair")
} }
func ErrCommissionNegative() error { func ErrCommissionNegative() sdk.Error {
return newError(CodeInvalidValidator, "Commission must be positive") return newError(CodeInvalidValidator, "Commission must be positive")
} }
func ErrCommissionHuge() error { func ErrCommissionHuge() sdk.Error {
return newError(CodeInvalidValidator, "Commission cannot be more than 100%") return newError(CodeInvalidValidator, "Commission cannot be more than 100%")
} }
func ErrBadValidatorAddr() error { func ErrBadValidatorAddr() sdk.Error {
return newError(CodeInvalidValidator, "Validator does not exist for that address") return newError(CodeInvalidValidator, "Validator does not exist for that address")
} }
func ErrCandidateExistsAddr() error { func ErrBadCandidateAddr() sdk.Error {
return newError(CodeInvalidValidator, "Candidate does not exist for that address")
}
func ErrCandidateExistsAddr() sdk.Error {
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy") return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
} }
func ErrMissingSignature() error { func ErrMissingSignature() sdk.Error {
return newError(CodeInvalidValidator, "Missing signature") return newError(CodeInvalidValidator, "Missing signature")
} }
func ErrBondNotNominated() error { func ErrBondNotNominated() sdk.Error {
return newError(CodeInvalidValidator, "Cannot bond to non-nominated account") return newError(CodeInvalidValidator, "Cannot bond to non-nominated account")
} }
func ErrNoCandidateForAddress() error { func ErrNoCandidateForAddress() sdk.Error {
return newError(CodeInvalidValidator, "Validator does not exist for that address") return newError(CodeInvalidValidator, "Validator does not exist for that address")
} }
func ErrNoDelegatorForAddress() error { func ErrNoDelegatorForAddress() sdk.Error {
return newError(CodeInvalidValidator, "Delegator does not contain validator bond") return newError(CodeInvalidValidator, "Delegator does not contain validator bond")
} }
func ErrInsufficientFunds() error { func ErrInsufficientFunds() sdk.Error {
return newError(CodeInvalidValidator, "Insufficient bond shares") return newError(CodeInvalidValidator, "Insufficient bond shares")
} }
func ErrBadRemoveValidator() error { func ErrBadRemoveValidator() sdk.Error {
return newError(CodeInvalidValidator, "Error removing validator") return newError(CodeInvalidValidator, "Error removing validator")
} }

View File

@ -1,7 +1,6 @@
package stake package stake
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
@ -9,28 +8,27 @@ import (
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
) )
// separated for testing // separated for testing
func InitState(ctx sdk.Context, mapper Mapper, key, value string) error { func InitState(ctx sdk.Context, mapper Mapper, key, value string) sdk.Error {
params := mapper.loadParams() params := mapper.loadParams()
switch key { switch key {
case "allowed_bond_denom": case "allowed_bond_denom":
params.AllowedBondDenom = value params.BondDenom = value
case "max_vals", "gas_bond", "gas_unbond": case "max_vals", "gas_bond", "gas_unbond":
i, err := strconv.Atoi(value) i, err := strconv.Atoi(value)
if err != nil { if err != nil {
return fmt.Errorf("input must be integer, Error: %v", err.Error()) return sdk.ErrUnknownRequest(fmt.Sprintf("input must be integer, Error: %v", err.Error()))
} }
switch key { switch key {
case "max_vals": case "max_vals":
if i < 0 { if i < 0 {
return errors.New("cannot designate negative max validators") return sdk.ErrUnknownRequest("cannot designate negative max validators")
} }
params.MaxVals = uint16(i) params.MaxVals = uint16(i)
case "gas_bond": case "gas_bond":
@ -53,112 +51,98 @@ func NewHandler(mapper Mapper, ck bank.CoinKeeper) sdk.Handler {
params := mapper.loadParams() params := mapper.loadParams()
res := msg.ValidateBasic().Result() err := msg.ValidateBasic()
if res.Code != sdk.CodeOK {
return res
}
sender, err := getTxSender(ctx)
if err != nil { if err != nil {
return return err.Result() // TODO should also return gasUsed?
} }
signers := msg.GetSigners()
if len(signers) != 1 {
return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
}
sender := signers[0]
transact := NewTransact(ctx, ck) transact := newTransact(ctx, sender, mapper, ck)
// Run the transaction // Run the transaction
switch _tx := tx.Unwrap().(type) { switch msg := msg.(type) {
case TxDeclareCandidacy: case MsgDeclareCandidacy:
res := transact.declareCandidacy(msg).Result()
if !ctx.IsCheckTx() { if !ctx.IsCheckTx() {
res.GasUsed = params.GasDeclareCandidacy res.GasUsed = params.GasDeclareCandidacy
} }
return res, transact.declareCandidacy(_tx) return res
case TxEditCandidacy: case MsgEditCandidacy:
res := transact.editCandidacy(msg).Result()
if !ctx.IsCheckTx() { if !ctx.IsCheckTx() {
res.GasUsed = params.GasEditCandidacy res.GasUsed = params.GasEditCandidacy
} }
return res, transact.editCandidacy(_tx) return res
case TxDelegate: case MsgDelegate:
res := transact.delegate(msg).Result()
if !ctx.IsCheckTx() { if !ctx.IsCheckTx() {
res.GasUsed = params.GasDelegate res.GasUsed = params.GasDelegate
} }
return res, transact.delegate(_tx) return res
case TxUnbond: case MsgUnbond:
//context with hold account permissions res := transact.unbond(msg).Result()
if !ctx.IsCheckTx() { if !ctx.IsCheckTx() {
params := loadParams(store)
res.GasUsed = params.GasUnbond res.GasUsed = params.GasUnbond
} }
return res, transact.unbond(_tx) return res
default: default:
return sdk.ErrUnknownTxType(msgType) return sdk.ErrTxParse("invalid message parse in staking module").Result()
} }
return
} }
} }
// get the sender from the ctx and ensure it matches the tx pubkey
func getTxSender(ctx sdk.Context) (sender crypto.Address, err error) {
senders := ctx.GetPermissions("", auth.NameSigs)
if len(senders) != 1 {
return sender, ErrMissingSignature()
}
return senders[0], nil
}
//_____________________________________________________________________ //_____________________________________________________________________
// common fields to all transactions // common fields to all transactions
type transact struct { type transact struct {
ctx sdk.Context
sender crypto.Address sender crypto.Address
mapper Mapper mapper Mapper
coinKeeper bank.CoinKeeper coinKeeper bank.CoinKeeper
params Params params Params
gs *GlobalState gs *GlobalState
isCheckTx sdk.Context
} }
func newTransact(ctx sdk.Context, sender sdk.Address, mapper Mapper, ck bank.CoinKeeper) transact { func newTransact(ctx sdk.Context, sender sdk.Address, mapper Mapper, ck bank.CoinKeeper) transact {
return transact{ return transact{
ctx: ctx,
sender: sender, sender: sender,
mapper: mapper, mapper: mapper,
coinKeeper: ck, coinKeeper: ck,
params: mapper.loadParams(), params: mapper.loadParams(),
gs: mapper.loadGlobalState(), gs: mapper.loadGlobalState(),
isCheckTx: ctx.IsCheckTx(),
} }
} }
//_____________________________________________________________________ //_____________________________________________________________________
// helper functions // helper functions
// TODO move from deliver with new SDK should only be dependant on store to send coins in NEW SDK
// move a candidates asset pool from bonded to unbonded pool // move a candidates asset pool from bonded to unbonded pool
func (tr transact) bondedToUnbondedPool(candidate *Candidate) error { func (tr transact) bondedToUnbondedPool(candidate *Candidate) {
// replace bonded shares with unbonded shares // replace bonded shares with unbonded shares
tokens := tr.gs.removeSharesBonded(candidate.Assets) tokens := tr.gs.removeSharesBonded(candidate.Assets)
candidate.Assets = tr.gs.addTokensUnbonded(tokens) candidate.Assets = tr.gs.addTokensUnbonded(tokens)
candidate.Status = Unbonded candidate.Status = Unbonded
return tr.transfer(tr.params.HoldBonded, tr.params.HoldUnbonded,
sdk.Coins{{tr.params.AllowedBondDenom, tokens}})
} }
// move a candidates asset pool from unbonded to bonded pool // move a candidates asset pool from unbonded to bonded pool
func (tr transact) unbondedToBondedPool(candidate *Candidate) error { func (tr transact) unbondedToBondedPool(candidate *Candidate) {
// replace bonded shares with unbonded shares // replace unbonded shares with bonded shares
tokens := tr.gs.removeSharesUnbonded(candidate.Assets) tokens := tr.gs.removeSharesUnbonded(candidate.Assets)
candidate.Assets = tr.gs.addTokensBonded(tokens) candidate.Assets = tr.gs.addTokensBonded(tokens)
candidate.Status = Bonded candidate.Status = Bonded
return tr.transfer(tr.params.HoldUnbonded, tr.params.HoldBonded,
sdk.Coins{{tr.params.AllowedBondDenom, tokens}})
} }
// return an error if the bonds coins are incorrect // return an error if the bonds coins are incorrect
func checkDenom(mapper Mapper, tx BondUpdate) error { func checkDenom(mapper Mapper, bond sdk.Coin) sdk.Error {
if tx.Bond.Denom != mapper.loadParams().AllowedBondDenom { if bond.Denom != mapper.loadParams().BondDenom {
return fmt.Errorf("Invalid coin denomination") return ErrBadBondingDenom()
} }
return nil return nil
} }
@ -167,48 +151,42 @@ func checkDenom(mapper Mapper, tx BondUpdate) error {
// These functions assume everything has been authenticated, // These functions assume everything has been authenticated,
// now we just perform action and save // now we just perform action and save
func (tr transact) declareCandidacy(tx TxDeclareCandidacy) error {
func (tr transact) declareCandidacy(tx MsgDeclareCandidacy) sdk.Error {
// check to see if the pubkey or sender has been registered before // check to see if the pubkey or sender has been registered before
if tr.mapper.loadCandidate(tx.PubKey) != nil { if tr.mapper.loadCandidate(tx.Address) != nil {
return fmt.Errorf("cannot bond to pubkey which is already declared candidacy"+ return ErrCandidateExistsAddr()
" PubKey %v already registered with %v candidate address",
candidate.PubKey, candidate.Owner)
} }
err := checkDenom(tx.BondUpdate, tr.mapper) err := checkDenom(tr.mapper, tx.Bond)
if err != nil { if err != nil {
return err return err
} }
if tr.IsCheckTx { if tr.ctx.IsCheckTx() {
return nil return nil
} }
// create and save the empty candidate
bond := tr.mapper.loadCandidate(tx.PubKey)
if bond != nil {
return ErrCandidateExistsAddr()
}
candidate := NewCandidate(tx.PubKey, tr.sender, tx.Description) candidate := NewCandidate(tx.PubKey, tr.sender, tx.Description)
tr.mapper.saveCandidate(candidate) tr.mapper.saveCandidate(candidate)
// move coins from the tr.sender account to a (self-bond) delegator account // move coins from the tr.sender account to a (self-bond) delegator account
// the candidate account and global shares are updated within here // the candidate account and global shares are updated within here
txDelegate := TxDelegate{tx.BondUpdate} txDelegate := NewMsgDelegate(tx.Address, tx.Bond)
return tr.delegateWithCandidate(txDelegate, candidate) return tr.delegateWithCandidate(txDelegate, candidate)
} }
func (tr transact) editCandidacy(tx TxEditCandidacy) error { func (tr transact) editCandidacy(tx MsgEditCandidacy) sdk.Error {
// candidate must already be registered // candidate must already be registered
if tr.mapper.loadCandidate(tx.PubKey) == nil { // does PubKey exist if tr.mapper.loadCandidate(tx.Address) == nil {
return fmt.Errorf("cannot delegate to non-existant PubKey %v", tx.PubKey) return ErrBadCandidateAddr()
} }
if tr.IsCheckTx { if tr.ctx.IsCheckTx() {
return nil return nil
} }
// Get the pubKey bond account // Get the pubKey bond account
candidate := tr.mapper.loadCandidate(tx.PubKey) candidate := tr.mapper.loadCandidate(tx.Address)
if candidate == nil { if candidate == nil {
return ErrBondNotNominated() return ErrBondNotNominated()
} }
@ -234,28 +212,28 @@ func (tr transact) editCandidacy(tx TxEditCandidacy) error {
return nil return nil
} }
func (tr transact) delegate(tx TxDelegate) error { func (tr transact) delegate(tx MsgDelegate) sdk.Error {
if tr.mapper.loadCandidate(tx.PubKey) == nil { // does PubKey exist if tr.mapper.loadCandidate(tx.Address) == nil { // does PubKey exist
return fmt.Errorf("cannot delegate to non-existant PubKey %v", tx.PubKey) return ErrBadCandidateAddr()
} }
err := checkDenom(tx.BondUpdate, tr.mapper) err := checkDenom(tr.mapper, tx.Bond)
if err != nil { if err != nil {
return err return err
} }
if tr.IsCheckTx { if tr.ctx.IsCheckTx() {
return nil return nil
} }
// Get the pubKey bond account // Get the pubKey bond account
candidate := tr.mapper.loadCandidate(tx.PubKey) candidate := tr.mapper.loadCandidate(tx.Address)
if candidate == nil { if candidate == nil {
return ErrBondNotNominated() return ErrBondNotNominated()
} }
return tr.delegateWithCandidate(tx, candidate) return tr.delegateWithCandidate(tx, candidate)
} }
func (tr transact) delegateWithCandidate(tx TxDelegate, candidate *Candidate) error { func (tr transact) delegateWithCandidate(tx MsgDelegate, candidate *Candidate) sdk.Error {
if candidate.Status == Revoked { //candidate has been withdrawn if candidate.Status == Revoked { //candidate has been withdrawn
return ErrBondNotNominated() return ErrBondNotNominated()
@ -268,37 +246,33 @@ func (tr transact) delegateWithCandidate(tx TxDelegate, candidate *Candidate) er
poolAccount = tr.params.HoldUnbonded poolAccount = tr.params.HoldUnbonded
} }
// XXX refactor all steps like this into GlobalState.addBondedTokens()
// Move coins from the delegator account to the bonded pool account
err := tr.transfer(tr.sender, poolAccount, sdk.Coins{tx.Bond})
if err != nil {
return err
}
// Get or create the delegator bond // Get or create the delegator bond
bond := tr.mapper.loadDelegatorBond(tr.sender, tx.PubKey) bond := tr.mapper.loadDelegatorBond(tr.sender, tx.Address)
if bond == nil { if bond == nil {
bond = &DelegatorBond{ bond = &DelegatorBond{
PubKey: tx.PubKey, PubKey: tx.Address,
Shares: sdk.ZeroRat, Shares: sdk.ZeroRat,
} }
} }
// Account new shares, save // Account new shares, save
bond.Shares = bond.Shares.Add(candidate.addTokens(tx.Bond.Amount, tr.gs)) err := bond.BondTokens(candidate, tx.Bond, tr)
tr.mapper.saveCandidate(candidate) if err != nil {
return err
}
tr.mapper.saveDelegatorBond(tr.sender, bond) tr.mapper.saveDelegatorBond(tr.sender, bond)
tr.mapper.saveCandidate(candidate)
tr.mapper.saveGlobalState(tr.gs) tr.mapper.saveGlobalState(tr.gs)
return nil return nil
} }
func (tr transact) unbond(tx TxUnbond) error { func (tr transact) unbond(tx MsgUnbond) sdk.Error {
// check if bond has any shares in it unbond // check if bond has any shares in it unbond
existingBond := tr.mapper.loadDelegatorBond(tr.sender, tx.PubKey) existingBond := tr.mapper.loadDelegatorBond(tr.sender, tx.Address)
sharesStr := viper.GetString(tx.Shares) sharesStr := viper.GetString(tx.Shares)
if existingBond.Shares.LT(sdk.ZeroRat) { // bond shares < tx shares if existingBond.Shares.LT(sdk.ZeroRat) { // bond shares < tx shares
return errors.New("no shares in account to unbond") return ErrInsufficientFunds()
} }
// if shares set to special case Max then we're good // if shares set to special case Max then we're good
@ -315,10 +289,12 @@ func (tr transact) unbond(tx TxUnbond) error {
bond.Shares, tx.Shares) bond.Shares, tx.Shares)
} }
} }
// XXX end of old checkTx if tr.ctx.IsCheckTx() {
return nil
}
// get delegator bond // get delegator bond
bond := tr.mapper.loadDelegatorBond(tr.sender, tx.PubKey) bond := tr.mapper.loadDelegatorBond(tr.sender, tx.Address)
if bond == nil { if bond == nil {
return ErrNoDelegatorForAddress() return ErrNoDelegatorForAddress()
} }
@ -328,7 +304,6 @@ func (tr transact) unbond(tx TxUnbond) error {
if tx.Shares == "MAX" { if tx.Shares == "MAX" {
shares = bond.Shares shares = bond.Shares
} else { } else {
var err error
shares, err = sdk.NewRatFromDecimal(tx.Shares) shares, err = sdk.NewRatFromDecimal(tx.Shares)
if err != nil { if err != nil {
return err return err
@ -342,7 +317,7 @@ func (tr transact) unbond(tx TxUnbond) error {
bond.Shares = bond.Shares.Sub(shares) bond.Shares = bond.Shares.Sub(shares)
// get pubKey candidate // get pubKey candidate
candidate := tr.mapper.loadCandidate(tx.PubKey) candidate := tr.mapper.loadCandidate(tx.Address)
if candidate == nil { if candidate == nil {
return ErrNoCandidateForAddress() return ErrNoCandidateForAddress()
} }
@ -358,7 +333,7 @@ func (tr transact) unbond(tx TxUnbond) error {
} }
// remove the bond // remove the bond
tr.mapper.removeDelegatorBond(tr.sender, tx.PubKey) tr.mapper.removeDelegatorBond(tr.sender, tx.Address)
} else { } else {
tr.mapper.saveDelegatorBond(tr.sender, bond) tr.mapper.saveDelegatorBond(tr.sender, bond)
} }
@ -373,7 +348,7 @@ func (tr transact) unbond(tx TxUnbond) error {
returnCoins := candidate.removeShares(shares, tr.gs) returnCoins := candidate.removeShares(shares, tr.gs)
err := tr.transfer(poolAccount, tr.sender, err := tr.transfer(poolAccount, tr.sender,
sdk.Coins{{tr.params.AllowedBondDenom, returnCoins}}) sdk.Coins{{tr.params.BondDenom, returnCoins}})
if err != nil { if err != nil {
return err return err
} }
@ -383,10 +358,7 @@ func (tr transact) unbond(tx TxUnbond) error {
// change the share types to unbonded if they were not already // change the share types to unbonded if they were not already
if candidate.Status == Bonded { if candidate.Status == Bonded {
err = tr.bondedToUnbondedPool(candidate) tr.bondedToUnbondedPool(candidate)
if err != nil {
return err
}
} }
// lastly update the status // lastly update the status
@ -395,7 +367,7 @@ func (tr transact) unbond(tx TxUnbond) error {
// deduct shares from the candidate and save // deduct shares from the candidate and save
if candidate.Liabilities.IsZero() { if candidate.Liabilities.IsZero() {
tr.mapper.removeCandidate(tx.PubKey) tr.mapper.removeCandidate(tx.Address)
} else { } else {
tr.mapper.saveCandidate(candidate) tr.mapper.saveCandidate(candidate)
} }

View File

@ -24,25 +24,24 @@ func initAccounts(n int, amount int64) ([]sdk.Address, map[string]int64) {
return senders, accStore return senders, accStore
} }
func newTxDeclareCandidacy(amt int64, pubKey crypto.PubKey) TxDeclareCandidacy { func newTestMsgDeclareCandidacy(amt int64, pubKey crypto.PubKey, address sdk.Address) MsgDeclareCandidacy {
return TxDeclareCandidacy{ return MsgDeclareCandidacy{
BondUpdate{ MsgAddr: NewMsgAddr(address),
PubKey: pubKey, PubKey: pubKey,
Bond: coin.Coin{"fermion", amt}, Bond: coin.Coin{"fermion", amt},
},
Description{}, Description{},
} }
} }
func newTxDelegate(amt int64, pubKey crypto.PubKey) TxDelegate { func newTestMsgDelegate(amt int64, address sdk.Address) MsgDelegate {
return TxDelegate{BondUpdate{ return MsgDelegate{
PubKey: pubKey, MsgAddr: NewMsgAddr(address),
Bond: coin.Coin{"fermion", amt}, Bond: coin.Coin{"fermion", amt},
}} }
} }
func newTxUnbond(shares string, pubKey crypto.PubKey) TxUnbond { func newMsgUnbond(shares string, pubKey crypto.PubKey) MsgUnbond {
return TxUnbond{ return MsgUnbond{
PubKey: pubKey, PubKey: pubKey,
Shares: shares, Shares: shares,
} }
@ -50,14 +49,12 @@ func newTxUnbond(shares string, pubKey crypto.PubKey) TxUnbond {
func paramsNoInflation() Params { func paramsNoInflation() Params {
return Params{ return Params{
HoldBonded: sdk.NewActor(stakingModuleName, []byte("77777777777777777777777777777777")),
HoldUnbonded: sdk.NewActor(stakingModuleName, []byte("88888888888888888888888888888888")),
InflationRateChange: sdk.Zero, InflationRateChange: sdk.Zero,
InflationMax: sdk.Zero, InflationMax: sdk.Zero,
InflationMin: sdk.Zero, InflationMin: sdk.Zero,
GoalBonded: sdk.New(67, 100), GoalBonded: sdk.New(67, 100),
MaxVals: 100, MaxVals: 100,
AllowedBondDenom: "fermion", BondDenom: "fermion",
GasDeclareCandidacy: 20, GasDeclareCandidacy: 20,
GasEditCandidacy: 20, GasEditCandidacy: 20,
GasDelegate: 20, GasDelegate: 20,
@ -72,7 +69,7 @@ func newTestTransact(t, sender sdk.Address, isCheckTx bool) transact {
newTransact(ctx, sender, mapper, coinKeeper) newTransact(ctx, sender, mapper, coinKeeper)
} }
func TestDuplicatesTxDeclareCandidacy(t *testing.T) { func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
senders, accStore := initAccounts(2, 1000) // for accounts senders, accStore := initAccounts(2, 1000) // for accounts
deliverer := newDeliver(t, senders[0], accStore) deliverer := newDeliver(t, senders[0], accStore)
@ -81,9 +78,9 @@ func TestDuplicatesTxDeclareCandidacy(t *testing.T) {
sender: senders[0], sender: senders[0],
} }
txDeclareCandidacy := newTxDeclareCandidacy(10, pks[0]) txDeclareCandidacy := newTestMsgDeclareCandidacy(10, pks[0])
got := deliverer.declareCandidacy(txDeclareCandidacy) got := deliverer.declareCandidacy(txDeclareCandidacy)
assert.NoError(t, got, "expected no error on runTxDeclareCandidacy") assert.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// one sender can bond to two different pubKeys // one sender can bond to two different pubKeys
txDeclareCandidacy.PubKey = pks[1] txDeclareCandidacy.PubKey = pks[1]
@ -97,21 +94,21 @@ func TestDuplicatesTxDeclareCandidacy(t *testing.T) {
assert.NotNil(t, err, "expected error on checkTx") assert.NotNil(t, err, "expected error on checkTx")
} }
func TestIncrementsTxDelegate(t *testing.T) { func TestIncrementsMsgDelegate(t *testing.T) {
initSender := int64(1000) initSender := int64(1000)
senders, accStore := initAccounts(1, initSender) // for accounts senders, accStore := initAccounts(1, initSender) // for accounts
deliverer := newDeliver(t, senders[0], accStore) deliverer := newDeliver(t, senders[0], accStore)
// first declare candidacy // first declare candidacy
bondAmount := int64(10) bondAmount := int64(10)
txDeclareCandidacy := newTxDeclareCandidacy(bondAmount, pks[0]) txDeclareCandidacy := newTestMsgDeclareCandidacy(bondAmount, pks[0])
got := deliverer.declareCandidacy(txDeclareCandidacy) got := deliverer.declareCandidacy(txDeclareCandidacy)
assert.NoError(t, got, "expected declare candidacy tx to be ok, got %v", got) assert.NoError(t, got, "expected declare candidacy tx to be ok, got %v", got)
expectedBond := bondAmount // 1 since we send 1 at the start of loop, expectedBond := bondAmount // 1 since we send 1 at the start of loop,
// just send the same txbond multiple times // just send the same txbond multiple times
holder := deliverer.params.HoldUnbonded // XXX this should be HoldBonded, new SDK updates holder := deliverer.params.HoldUnbonded // XXX this should be HoldBonded, new SDK updates
txDelegate := newTxDelegate(bondAmount, pks[0]) txDelegate := newTestMsgDelegate(bondAmount, pks[0])
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
got := deliverer.delegate(txDelegate) got := deliverer.delegate(txDelegate)
assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got) assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got)
@ -129,7 +126,7 @@ func TestIncrementsTxDelegate(t *testing.T) {
} }
} }
func TestIncrementsTxUnbond(t *testing.T) { func TestIncrementsMsgUnbond(t *testing.T) {
initSender := int64(0) initSender := int64(0)
senders, accStore := initAccounts(1, initSender) // for accounts senders, accStore := initAccounts(1, initSender) // for accounts
deliverer := newDeliver(t, senders[0], accStore) deliverer := newDeliver(t, senders[0], accStore)
@ -137,7 +134,7 @@ func TestIncrementsTxUnbond(t *testing.T) {
// set initial bond // set initial bond
initBond := int64(1000) initBond := int64(1000)
accStore[string(deliverer.sender.Address)] = initBond accStore[string(deliverer.sender.Address)] = initBond
got := deliverer.declareCandidacy(newTxDeclareCandidacy(initBond, pks[0])) got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(initBond, pks[0]))
assert.NoError(t, got, "expected initial bond tx to be ok, got %v", got) assert.NoError(t, got, "expected initial bond tx to be ok, got %v", got)
// just send the same txunbond multiple times // just send the same txunbond multiple times
@ -145,7 +142,7 @@ func TestIncrementsTxUnbond(t *testing.T) {
// XXX use decimals here // XXX use decimals here
unbondShares, unbondSharesStr := int64(10), "10" unbondShares, unbondSharesStr := int64(10), "10"
txUndelegate := newTxUnbond(unbondSharesStr, pks[0]) txUndelegate := newMsgUnbond(unbondSharesStr, pks[0])
nUnbonds := 5 nUnbonds := 5
for i := 0; i < nUnbonds; i++ { for i := 0; i < nUnbonds; i++ {
got := deliverer.unbond(txUndelegate) got := deliverer.unbond(txUndelegate)
@ -174,7 +171,7 @@ func TestIncrementsTxUnbond(t *testing.T) {
} }
for _, c := range errorCases { for _, c := range errorCases {
unbondShares := strconv.Itoa(int(c)) unbondShares := strconv.Itoa(int(c))
txUndelegate := newTxUnbond(unbondShares, pks[0]) txUndelegate := newMsgUnbond(unbondShares, pks[0])
got = deliverer.unbond(txUndelegate) got = deliverer.unbond(txUndelegate)
assert.Error(t, got, "expected unbond tx to fail") assert.Error(t, got, "expected unbond tx to fail")
} }
@ -182,17 +179,17 @@ func TestIncrementsTxUnbond(t *testing.T) {
leftBonded := initBond - unbondShares*int64(nUnbonds) leftBonded := initBond - unbondShares*int64(nUnbonds)
// should be unable to unbond one more than we have // should be unable to unbond one more than we have
txUndelegate = newTxUnbond(strconv.Itoa(int(leftBonded)+1), pks[0]) txUndelegate = newMsgUnbond(strconv.Itoa(int(leftBonded)+1), pks[0])
got = deliverer.unbond(txUndelegate) got = deliverer.unbond(txUndelegate)
assert.Error(t, got, "expected unbond tx to fail") assert.Error(t, got, "expected unbond tx to fail")
// should be able to unbond just what we have // should be able to unbond just what we have
txUndelegate = newTxUnbond(strconv.Itoa(int(leftBonded)), pks[0]) txUndelegate = newMsgUnbond(strconv.Itoa(int(leftBonded)), pks[0])
got = deliverer.unbond(txUndelegate) got = deliverer.unbond(txUndelegate)
assert.NoError(t, got, "expected unbond tx to pass") assert.NoError(t, got, "expected unbond tx to pass")
} }
func TestMultipleTxDeclareCandidacy(t *testing.T) { func TestMultipleMsgDeclareCandidacy(t *testing.T) {
initSender := int64(1000) initSender := int64(1000)
senders, accStore := initAccounts(3, initSender) senders, accStore := initAccounts(3, initSender)
pubKeys := []crypto.PubKey{pks[0], pks[1], pks[2]} pubKeys := []crypto.PubKey{pks[0], pks[1], pks[2]}
@ -200,7 +197,7 @@ func TestMultipleTxDeclareCandidacy(t *testing.T) {
// bond them all // bond them all
for i, sender := range senders { for i, sender := range senders {
txDeclareCandidacy := newTxDeclareCandidacy(10, pubKeys[i]) txDeclareCandidacy := newTestMsgDeclareCandidacy(10, pubKeys[i])
deliverer.sender = sender deliverer.sender = sender
got := deliverer.declareCandidacy(txDeclareCandidacy) got := deliverer.declareCandidacy(txDeclareCandidacy)
assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got) assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got)
@ -217,7 +214,7 @@ func TestMultipleTxDeclareCandidacy(t *testing.T) {
// unbond them all // unbond them all
for i, sender := range senders { for i, sender := range senders {
candidatePre := loadCandidate(deliverer.store, pubKeys[i]) candidatePre := loadCandidate(deliverer.store, pubKeys[i])
txUndelegate := newTxUnbond("10", pubKeys[i]) txUndelegate := newMsgUnbond("10", pubKeys[i])
deliverer.sender = sender deliverer.sender = sender
got := deliverer.unbond(txUndelegate) got := deliverer.unbond(txUndelegate)
assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got) assert.NoError(t, got, "expected tx %d to be ok, got %v", i, got)
@ -233,19 +230,19 @@ func TestMultipleTxDeclareCandidacy(t *testing.T) {
} }
} }
func TestMultipleTxDelegate(t *testing.T) { func TestMultipleMsgDelegate(t *testing.T) {
accounts, accStore := initAccounts(3, 1000) accounts, accStore := initAccounts(3, 1000)
sender, delegators := accounts[0], accounts[1:] sender, delegators := accounts[0], accounts[1:]
deliverer := newDeliver(t, sender, accStore) deliverer := newDeliver(t, sender, accStore)
//first make a candidate //first make a candidate
txDeclareCandidacy := newTxDeclareCandidacy(10, pks[0]) txDeclareCandidacy := newTestMsgDeclareCandidacy(10, pks[0])
got := deliverer.declareCandidacy(txDeclareCandidacy) got := deliverer.declareCandidacy(txDeclareCandidacy)
require.NoError(t, got, "expected tx to be ok, got %v", got) require.NoError(t, got, "expected tx to be ok, got %v", got)
// delegate multiple parties // delegate multiple parties
for i, delegator := range delegators { for i, delegator := range delegators {
txDelegate := newTxDelegate(10, pks[0]) txDelegate := newTestMsgDelegate(10, pks[0])
deliverer.sender = delegator deliverer.sender = delegator
got := deliverer.delegate(txDelegate) got := deliverer.delegate(txDelegate)
require.NoError(t, got, "expected tx %d to be ok, got %v", i, got) require.NoError(t, got, "expected tx %d to be ok, got %v", i, got)
@ -257,7 +254,7 @@ func TestMultipleTxDelegate(t *testing.T) {
// unbond them all // unbond them all
for i, delegator := range delegators { for i, delegator := range delegators {
txUndelegate := newTxUnbond("10", pks[0]) txUndelegate := newMsgUnbond("10", pks[0])
deliverer.sender = delegator deliverer.sender = delegator
got := deliverer.unbond(txUndelegate) got := deliverer.unbond(txUndelegate)
require.NoError(t, got, "expected tx %d to be ok, got %v", i, got) require.NoError(t, got, "expected tx %d to be ok, got %v", i, got)
@ -274,21 +271,21 @@ func TestVoidCandidacy(t *testing.T) {
deliverer := newDeliver(t, sender, accStore) deliverer := newDeliver(t, sender, accStore)
// create the candidate // create the candidate
txDeclareCandidacy := newTxDeclareCandidacy(10, pks[0]) txDeclareCandidacy := newTestMsgDeclareCandidacy(10, pks[0])
got := deliverer.declareCandidacy(txDeclareCandidacy) got := deliverer.declareCandidacy(txDeclareCandidacy)
require.NoError(t, got, "expected no error on runTxDeclareCandidacy") require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// bond a delegator // bond a delegator
txDelegate := newTxDelegate(10, pks[0]) txDelegate := newTestMsgDelegate(10, pks[0])
deliverer.sender = delegator deliverer.sender = delegator
got = deliverer.delegate(txDelegate) got = deliverer.delegate(txDelegate)
require.NoError(t, got, "expected ok, got %v", got) require.NoError(t, got, "expected ok, got %v", got)
// unbond the candidates bond portion // unbond the candidates bond portion
txUndelegate := newTxUnbond("10", pks[0]) txUndelegate := newMsgUnbond("10", pks[0])
deliverer.sender = sender deliverer.sender = sender
got = deliverer.unbond(txUndelegate) got = deliverer.unbond(txUndelegate)
require.NoError(t, got, "expected no error on runTxDeclareCandidacy") require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// test that this pubkey cannot yet be bonded too // test that this pubkey cannot yet be bonded too
deliverer.sender = delegator deliverer.sender = delegator
@ -297,7 +294,7 @@ func TestVoidCandidacy(t *testing.T) {
// test that the delegator can still withdraw their bonds // test that the delegator can still withdraw their bonds
got = deliverer.unbond(txUndelegate) got = deliverer.unbond(txUndelegate)
require.NoError(t, got, "expected no error on runTxDeclareCandidacy") require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// verify that the pubkey can now be reused // verify that the pubkey can now be reused
got = deliverer.declareCandidacy(txDeclareCandidacy) got = deliverer.declareCandidacy(txDeclareCandidacy)

View File

@ -1,8 +1,6 @@
package stake package stake
import ( import (
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
) )
@ -10,9 +8,9 @@ import (
//nolint //nolint
var ( var (
// Keys for store prefixes // Keys for store prefixes
CandidatesPubKeysKey = []byte{0x01} // key for all candidates' pubkeys CandidatesAddrKey = []byte{0x01} // key for all candidates' addresses
ParamKey = []byte{0x02} // key for global parameters relating to staking ParamKey = []byte{0x02} // key for global parameters relating to staking
GlobalStateKey = []byte{0x03} // key for global parameters relating to staking GlobalStateKey = []byte{0x03} // key for global parameters relating to staking
// Key prefixes // Key prefixes
CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate
@ -22,29 +20,29 @@ var (
DelegatorBondsKeyPrefix = []byte{0x08} // prefix for each key to a delegator's bond DelegatorBondsKeyPrefix = []byte{0x08} // prefix for each key to a delegator's bond
) )
// GetCandidateKey - get the key for the candidate with pubKey // GetCandidateKey - get the key for the candidate with address
func GetCandidateKey(pubKey crypto.PubKey) []byte { func GetCandidateKey(address sdk.Address) []byte {
return append(CandidateKeyPrefix, pubKey.Bytes()...) return append(CandidateKeyPrefix, address.Bytes()...)
} }
// GetValidatorKey - get the key for the validator used in the power-store // GetValidatorKey - get the key for the validator used in the power-store
func GetValidatorKey(pubKey crypto.PubKey, power sdk.Rational) []byte { func GetValidatorKey(address sdk.Address, power sdk.Rational) []byte {
b, _ := cdc.MarshalJSON(power) // TODO need to handle error here? b, _ := cdc.MarshalJSON(power) // TODO need to handle error here?
return append(ValidatorKeyPrefix, append(b, pubKey.Bytes()...)...) // TODO does this need prefix if its in its own store return append(ValidatorKeyPrefix, append(b, address.Bytes()...)...) // TODO does this need prefix if its in its own store
} }
// GetValidatorUpdatesKey - get the key for the validator used in the power-store // GetValidatorUpdatesKey - get the key for the validator used in the power-store
func GetValidatorUpdatesKey(pubKey crypto.PubKey) []byte { func GetValidatorUpdatesKey(address sdk.Address) []byte {
return append(ValidatorUpdatesKeyPrefix, pubKey.Bytes()...) // TODO does this need prefix if its in its own store return append(ValidatorUpdatesKeyPrefix, address.Bytes()...) // TODO does this need prefix if its in its own store
} }
// GetDelegatorBondKey - get the key for delegator bond with candidate // GetDelegatorBondKey - get the key for delegator bond with candidate
func GetDelegatorBondKey(delegator crypto.Address, candidate crypto.PubKey) []byte { func GetDelegatorBondKey(delegator, candidate sdk.Address) []byte {
return append(GetDelegatorBondKeyPrefix(delegator), candidate.Bytes()...) return append(GetDelegatorBondKeyPrefix(delegator), candidate.Bytes()...)
} }
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates // GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
func GetDelegatorBondKeyPrefix(delegator crypto.Address) []byte { func GetDelegatorBondKeyPrefix(delegator sdk.Address) []byte {
res, err := cdc.MarshalJSON(&delegator) res, err := cdc.MarshalJSON(&delegator)
if err != nil { if err != nil {
panic(err) panic(err)
@ -53,7 +51,7 @@ func GetDelegatorBondKeyPrefix(delegator crypto.Address) []byte {
} }
// GetDelegatorBondsKey - get the key for list of all the delegator's bonds // GetDelegatorBondsKey - get the key for list of all the delegator's bonds
func GetDelegatorBondsKey(delegator crypto.Address) []byte { func GetDelegatorBondsKey(delegator sdk.Address) []byte {
res, err := cdc.MarshalJSON(&delegator) res, err := cdc.MarshalJSON(&delegator)
if err != nil { if err != nil {
panic(err) panic(err)
@ -69,20 +67,15 @@ type Mapper struct {
cdc *wire.Codec cdc *wire.Codec
} }
func NewMapper(ctx sdk.Context, key sdk.StoreKey) Mapper { func NewMapper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey) Mapper {
cdc := wire.NewCodec()
cdc.RegisterInterface((*sdk.Rational)(nil), nil) // XXX make like crypto.RegisterWire()
cdc.RegisterConcrete(sdk.Rat{}, "rat", nil)
crypto.RegisterWire(cdc)
return StakeMapper{ return StakeMapper{
store: ctx.KVStore(m.key), store: ctx.KVStore(m.key),
cdc: cdc, cdc: cdc,
} }
} }
func (m Mapper) loadCandidate(pubKey crypto.PubKey) *Candidate { func (m Mapper) loadCandidate(address sdk.Address) *Candidate {
b := m.store.Get(GetCandidateKey(pubKey)) b := m.store.Get(GetCandidateKey(address))
if b == nil { if b == nil {
return nil return nil
} }
@ -97,28 +90,28 @@ func (m Mapper) loadCandidate(pubKey crypto.PubKey) *Candidate {
func (m Mapper) saveCandidate(candidate *Candidate) { func (m Mapper) saveCandidate(candidate *Candidate) {
// XXX should only remove validator if we know candidate is a validator // XXX should only remove validator if we know candidate is a validator
removeValidator(m.store, candidate.PubKey) removeValidator(m.store, candidate.Address)
validator := &Validator{candidate.PubKey, candidate.VotingPower} validator := &Validator{candidate.Address, candidate.VotingPower}
updateValidator(m.store, validator) updateValidator(m.store, validator)
b, err := cdc.MarshalJSON(*candidate) b, err := cdc.MarshalJSON(*candidate)
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.store.Set(GetCandidateKey(candidate.PubKey), b) m.store.Set(GetCandidateKey(candidate.Address), b)
} }
func (m Mapper) removeCandidate(pubKey crypto.PubKey) { func (m Mapper) removeCandidate(address sdk.Address) {
// XXX should only remove validator if we know candidate is a validator // XXX should only remove validator if we know candidate is a validator
removeValidator(m.store, pubKey) removeValidator(m.store, address)
m.store.Delete(GetCandidateKey(pubKey)) m.store.Delete(GetCandidateKey(address))
} }
//___________________________________________________________________________ //___________________________________________________________________________
//func loadValidator(m.store sdk.KVStore, pubKey crypto.PubKey, votingPower sdk.Rational) *Validator { //func loadValidator(m.store sdk.KVStore, address sdk.Address, votingPower sdk.Rational) *Validator {
//b := m.store.Get(GetValidatorKey(pubKey, votingPower)) //b := m.store.Get(GetValidatorKey(address, votingPower))
//if b == nil { //if b == nil {
//return nil //return nil
//} //}
@ -140,25 +133,25 @@ func (m Mapper) updateValidator(validator *Validator) {
} }
// add to the validators to update list if necessary // add to the validators to update list if necessary
m.store.Set(GetValidatorUpdatesKey(validator.PubKey), b) m.store.Set(GetValidatorUpdatesKey(validator.Address), b)
// update the list ordered by voting power // update the list ordered by voting power
m.store.Set(GetValidatorKey(validator.PubKey, validator.VotingPower), b) m.store.Set(GetValidatorKey(validator.Address, validator.VotingPower), b)
} }
func (m Mapper) removeValidator(pubKey crypto.PubKey) { func (m Mapper) removeValidator(address sdk.Address) {
//add validator with zero power to the validator updates //add validator with zero power to the validator updates
b, err := cdc.MarshalJSON(Validator{pubKey, sdk.ZeroRat}) b, err := cdc.MarshalJSON(Validator{address, sdk.ZeroRat})
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.store.Set(GetValidatorUpdatesKey(pubKey), b) m.store.Set(GetValidatorUpdatesKey(address), b)
// now actually delete from the validator set // now actually delete from the validator set
candidate := loadCandidate(m.store, pubKey) candidate := loadCandidate(m.store, address)
if candidate != nil { if candidate != nil {
m.store.Delete(GetValidatorKey(pubKey, candidate.VotingPower)) m.store.Delete(GetValidatorKey(address, candidate.VotingPower))
} }
} }
@ -243,14 +236,14 @@ func (m Mapper) loadCandidates() (candidates Candidates) {
//_____________________________________________________________________ //_____________________________________________________________________
// load the pubkeys of all candidates a delegator is delegated too // load the pubkeys of all candidates a delegator is delegated too
func (m Mapper) loadDelegatorCandidates(delegator crypto.Address) (candidates []crypto.PubKey) { func (m Mapper) loadDelegatorCandidates(delegator sdk.Address) (candidateAddrs []sdk.Address) {
candidateBytes := m.store.Get(GetDelegatorBondsKey(delegator)) candidateBytes := m.store.Get(GetDelegatorBondsKey(delegator))
if candidateBytes == nil { if candidateBytes == nil {
return nil return nil
} }
err := cdc.UnmarshalJSON(candidateBytes, &candidates) err := cdc.UnmarshalJSON(candidateBytes, &candidateAddrs)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -259,8 +252,7 @@ func (m Mapper) loadDelegatorCandidates(delegator crypto.Address) (candidates []
//_____________________________________________________________________ //_____________________________________________________________________
func (m Mapper) loadDelegatorBond(delegator crypto.Address, func (m Mapper) loadDelegatorBond(delegator, candidate sdk.Address) *DelegatorBond {
candidate crypto.PubKey) *DelegatorBond {
delegatorBytes := m.store.Get(GetDelegatorBondKey(delegator, candidate)) delegatorBytes := m.store.Get(GetDelegatorBondKey(delegator, candidate))
if delegatorBytes == nil { if delegatorBytes == nil {
@ -275,13 +267,13 @@ func (m Mapper) loadDelegatorBond(delegator crypto.Address,
return bond return bond
} }
func (m Mapper) saveDelegatorBond(delegator crypto.Address, func (m Mapper) saveDelegatorBond(delegator sdk.Address,
bond *DelegatorBond) { bond *DelegatorBond) {
// if a new bond add to the list of bonds // if a new bond add to the list of bonds
if loadDelegatorBond(m.store, delegator, bond.PubKey) == nil { if loadDelegatorBond(m.store, delegator, bond.Address) == nil {
pks := loadDelegatorCandidates(m.store, delegator) pks := loadDelegatorCandidates(m.store, delegator)
pks = append(pks, (*bond).PubKey) pks = append(pks, (*bond).Address)
b, err := cdc.MarshalJSON(pks) b, err := cdc.MarshalJSON(pks)
if err != nil { if err != nil {
panic(err) panic(err)
@ -294,11 +286,11 @@ func (m Mapper) saveDelegatorBond(delegator crypto.Address,
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.store.Set(GetDelegatorBondKey(delegator, bond.PubKey), b) m.store.Set(GetDelegatorBondKey(delegator, bond.Address), b)
//updateDelegatorBonds(store, delegator) //updateDelegatorBonds(store, delegator)
} }
func (m Mapper) removeDelegatorBond(delegator crypto.Address, candidate crypto.PubKey) { func (m Mapper) removeDelegatorBond(delegator sdk.Address, candidate sdk.Address) {
// TODO use list queries on multistore to remove iterations here! // TODO use list queries on multistore to remove iterations here!
// first remove from the list of bonds // first remove from the list of bonds
pks := loadDelegatorCandidates(m.store, delegator) pks := loadDelegatorCandidates(m.store, delegator)

View File

@ -1,52 +1,204 @@
package stake package stake
import ( import (
"encoding/json"
"fmt" "fmt"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
) )
// Tx
//--------------------------------------------------------------------------------
// register the tx type with its validation logic
// make sure to use the name of the handler as the prefix in the tx type,
// so it gets routed properly
const (
ByteTxDeclareCandidacy = 0x55
ByteTxEditCandidacy = 0x56
ByteTxDelegate = 0x57
ByteTxUnbond = 0x58
TypeTxDeclareCandidacy = "staking/declareCandidacy"
TypeTxEditCandidacy = "staking/editCandidacy"
TypeTxDelegate = "staking/delegate"
TypeTxUnbond = "staking/unbond"
)
//func init() {
//sdk.TxMapper.RegisterImplementation(TxDeclareCandidacy{}, TypeTxDeclareCandidacy, ByteTxDeclareCandidacy)
//sdk.TxMapper.RegisterImplementation(TxEditCandidacy{}, TypeTxEditCandidacy, ByteTxEditCandidacy)
//sdk.TxMapper.RegisterImplementation(TxDelegate{}, TypeTxDelegate, ByteTxDelegate)
//sdk.TxMapper.RegisterImplementation(TxUnbond{}, TypeTxUnbond, ByteTxUnbond)
//}
//Verify interface at compile time //Verify interface at compile time
//var _, _, _, _ sdk.TxInner = &TxDeclareCandidacy{}, &TxEditCandidacy{}, &TxDelegate{}, &TxUnbond{} var _, _, _, _ sdk.Msg = &MsgDeclareCandidacy{}, &MsgEditCandidacy{}, &MsgDelegate{}, &MsgUnbond{}
// BondUpdate - struct for bonding or unbonding transactions //______________________________________________________________________
type BondUpdate struct {
PubKey crypto.PubKey `json:"pub_key"` // MsgAddr - struct for bonding or unbonding transactions
Bond sdk.Coin `json:"amount"` type MsgAddr struct {
Address sdk.Address `json:"address"`
}
func NewMsgAddr(address sdk.Address) MsgAddr {
return MsgAddr{
Address: address,
}
}
// nolint
func (msg MsgAddr) Type() string { return "stake" }
func (msg MsgAddr) Get(key interface{}) (value interface{}) { return nil }
func (msg MsgAddr) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} }
func (msg MsgAddr) String() string {
return fmt.Sprintf("MsgAddr{Address: %v}", msg.Address)
} }
// ValidateBasic - Check for non-empty candidate, and valid coins // ValidateBasic - Check for non-empty candidate, and valid coins
func (tx BondUpdate) ValidateBasic() error { func (msg MsgAddr) ValidateBasic() sdk.Error {
if tx.PubKey.Empty() { if msg.Address.Empty() {
return errCandidateEmpty return errCandidateEmpty
} }
coins := sdk.Coins{tx.Bond} }
//______________________________________________________________________
// MsgDeclareCandidacy - struct for unbonding transactions
type MsgDeclareCandidacy struct {
MsgAddr
Description
Bond sdk.Coin `json:"bond"`
PubKey crypto.PubKey `json:"pubkey"`
}
func NewMsgDeclareCandidacy(bond sdk.Coin, address sdk.Address, pubkey crypto.PubKey, description Description) MsgDeclareCandidacy {
return MsgDeclareCandidacy{
MsgAddr: NewMsgAddr(address),
Description: description,
Bond: bond,
PubKey: PubKey,
}
}
// get the bytes for the message signer to sign on
func (msg MsgDeclareCandidacy) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error {
err := MsgAddr.ValidateBasic()
if err != nil {
return err
}
err := validateCoin(msg.Bond)
if err != nil {
return err
}
empty := Description{}
if msg.Description == empty {
return fmt.Errorf("description must be included")
}
return nil
}
//______________________________________________________________________
// MsgEditCandidacy - struct for editing a candidate
type MsgEditCandidacy struct {
MsgAddr
Description
}
func NewMsgEditCandidacy(address sdk.Address, description Description) MsgEditCandidacy {
return MsgEditCandidacy{
MsgAddr: NewMsgAddr(address),
Description: description,
}
}
// get the bytes for the message signer to sign on
func (msg MsgEditCandidacy) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgEditCandidacy) ValidateBasic() sdk.Error {
err := MsgAddr.ValidateBasic()
if err != nil {
return err
}
empty := Description{}
if msg.Description == empty {
return fmt.Errorf("Transaction must include some information to modify")
}
return nil
}
//______________________________________________________________________
// MsgDelegate - struct for bonding transactions
type MsgDelegate struct {
MsgAddr
Bond sdk.Coin `json:"bond"`
}
func NewMsgDelegate(address sdk.Address, bond sdk.Coin) MsgDelegate {
return MsgDelegate{
MsgAddr: NewMsgAddr(address),
Bond: bond,
}
}
// get the bytes for the message signer to sign on
func (msg MsgDelegate) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgDelegate) ValidateBasic() sdk.Error {
err := MsgAddr.ValidateBasic()
if err != nil {
return err
}
err := validateCoin(msg.Bond)
if err != nil {
return err
}
return nil
}
//______________________________________________________________________
// MsgUnbond - struct for unbonding transactions
type MsgUnbond struct {
MsgAddr
Shares string `json:"shares"`
}
func NewMsgUnbond(shares string, address sdk.Address) MsgDelegate {
return MsgUnbond{
MsgAddr: NewMsgAddr(address),
Shares: shares,
}
}
// get the bytes for the message signer to sign on
func (msg MsgUnbond) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgUnbond) ValidateBasic() sdk.Error {
err := MsgAddr.ValidateBasic()
if err != nil {
return err
}
if msg.Shares {
return ErrCandidateEmpty()
}
return nil
}
//______________________________________________________________________
// helper
func validateCoin(coin coin.Coin) sdk.Error {
coins := sdk.Coins{bond}
if !sdk.IsValid() { if !sdk.IsValid() {
return sdk.ErrInvalidCoins() return sdk.ErrInvalidCoins()
} }
@ -55,92 +207,3 @@ func (tx BondUpdate) ValidateBasic() error {
} }
return nil return nil
} }
// TxDeclareCandidacy - struct for unbonding transactions
type TxDeclareCandidacy struct {
BondUpdate
Description
}
// NewTxDeclareCandidacy - new TxDeclareCandidacy
func NewTxDeclareCandidacy(bond sdk.Coin, pubKey crypto.PubKey, description Description) sdk.Tx {
return TxDeclareCandidacy{
BondUpdate{
PubKey: pubKey,
Bond: bond,
},
description,
}.Wrap()
}
// Wrap - Wrap a Tx as a Basecoin Tx
func (tx TxDeclareCandidacy) Wrap() sdk.Tx { return sdk.Tx{tx} }
// TxEditCandidacy - struct for editing a candidate
type TxEditCandidacy struct {
PubKey crypto.PubKey `json:"pub_key"`
Description
}
// NewTxEditCandidacy - new TxEditCandidacy
func NewTxEditCandidacy(pubKey crypto.PubKey, description Description) sdk.Tx {
return TxEditCandidacy{
PubKey: pubKey,
Description: description,
}.Wrap()
}
// Wrap - Wrap a Tx as a Basecoin Tx
func (tx TxEditCandidacy) Wrap() sdk.Tx { return sdk.Tx{tx} }
// ValidateBasic - Check for non-empty candidate,
func (tx TxEditCandidacy) ValidateBasic() error {
if tx.PubKey.Empty() {
return errCandidateEmpty
}
empty := Description{}
if tx.Description == empty {
return fmt.Errorf("Transaction must include some information to modify")
}
return nil
}
// TxDelegate - struct for bonding transactions
type TxDelegate struct{ BondUpdate }
// NewTxDelegate - new TxDelegate
func NewTxDelegate(bond sdk.Coin, pubKey crypto.PubKey) sdk.Tx {
return TxDelegate{BondUpdate{
PubKey: pubKey,
Bond: bond,
}}.Wrap()
}
// Wrap - Wrap a Tx as a Basecoin Tx
func (tx TxDelegate) Wrap() sdk.Tx { return sdk.Tx{tx} }
// TxUnbond - struct for unbonding transactions
type TxUnbond struct {
PubKey crypto.PubKey `json:"pub_key"`
Shares string `json:"amount"`
}
// NewTxUnbond - new TxUnbond
func NewTxUnbond(shares string, pubKey crypto.PubKey) sdk.Tx {
return TxUnbond{
PubKey: pubKey,
Shares: shares,
}.Wrap()
}
// Wrap - Wrap a Tx as a Basecoin Tx
func (tx TxUnbond) Wrap() sdk.Tx { return sdk.Tx{tx} }
// ValidateBasic - Check for non-empty candidate, positive shares
func (tx TxUnbond) ValidateBasic() error {
if tx.PubKey.Empty() {
return errCandidateEmpty
}
return nil
}

View File

@ -24,29 +24,40 @@ var (
coinNegNotAtoms = sdk.Coin{"foo", -10000} coinNegNotAtoms = sdk.Coin{"foo", -10000}
) )
func TestBondUpdateValidateBasic(t *testing.T) { func TestMsgAddrValidateBasic(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
PubKey crypto.PubKey address sdk.Address
Bond sdk.Coin
wantErr bool wantErr bool
}{ }{
{"basic good", pks[0], coinPos, false}, {"basic good", pks[0], false},
{"empty delegator", crypto.PubKey{}, coinPos, true}, {"empty delegator", crypto.PubKey{}, true},
{"zero coin", pks[0], coinZero, true},
{"neg coin", pks[0], coinNeg, true},
} }
for _, tc := range tests { for _, tc := range tests {
tx := TxDelegate{BondUpdate{ tx := NewMsgAddr(tc.address)
PubKey: tc.PubKey,
Bond: tc.Bond,
}}
assert.Equal(t, tc.wantErr, tx.ValidateBasic() != nil, assert.Equal(t, tc.wantErr, tx.ValidateBasic() != nil,
"test: %v, tx.ValidateBasic: %v", tc.name, tx.ValidateBasic()) "test: %v, tx.ValidateBasic: %v", tc.name, tx.ValidateBasic())
} }
} }
func TestValidateCoin(t *testing.T) {
tests := []struct {
name string
coin sdk.Coin
wantErr bool
}{
{"basic good", coinPos, false},
{"zero coin", coinZero, true},
{"neg coin", coinNeg, true},
}
for _, tc := range tests {
assert.Equal(t, tc.wantErr, tx.validateCoin(tc.coin) != nil,
"test: %v, tx.ValidateBasic: %v", tc.name, tx.ValidateBasic())
}
}
func TestAllAreTx(t *testing.T) { func TestAllAreTx(t *testing.T) {
// make sure all types construct properly // make sure all types construct properly
@ -54,23 +65,20 @@ func TestAllAreTx(t *testing.T) {
bondAmt := 1234321 bondAmt := 1234321
bond := sdk.Coin{Denom: "ATOM", Amount: int64(bondAmt)} bond := sdk.Coin{Denom: "ATOM", Amount: int64(bondAmt)}
// Note that Wrap is only defined on BondUpdate, so when you call it, txDelegate := NewMsgDelegate(bond, pubKey)
// you lose all info on the embedding type. Please add Wrap() _, ok := txDelegate.(MsgDelegate)
// method to all the parents
txDelegate := NewTxDelegate(bond, pubKey)
_, ok := txDelegate.Unwrap().(TxDelegate)
assert.True(t, ok, "%#v", txDelegate) assert.True(t, ok, "%#v", txDelegate)
txUnbond := NewTxUnbond(strconv.Itoa(bondAmt), pubKey) txUnbond := NewMsgUnbond(strconv.Itoa(bondAmt), pubKey)
_, ok = txUnbond.Unwrap().(TxUnbond) _, ok = txUnbond.(MsgUnbond)
assert.True(t, ok, "%#v", txUnbond) assert.True(t, ok, "%#v", txUnbond)
txDecl := NewTxDeclareCandidacy(bond, pubKey, Description{}) txDecl := NewMsgDeclareCandidacy(bond, pubKey, Description{})
_, ok = txDecl.Unwrap().(TxDeclareCandidacy) _, ok = txDecl.(MsgDeclareCandidacy)
assert.True(t, ok, "%#v", txDecl) assert.True(t, ok, "%#v", txDecl)
txEditCan := NewTxEditCandidacy(pubKey, Description{}) txEditCan := NewMsgEditCandidacy(pubKey, Description{})
_, ok = txEditCan.Unwrap().(TxEditCandidacy) _, ok = txEditCan.(MsgEditCandidacy)
assert.True(t, ok, "%#v", txEditCan) assert.True(t, ok, "%#v", txEditCan)
} }
@ -84,9 +92,9 @@ func TestSerializeTx(t *testing.T) {
tests := []struct { tests := []struct {
tx sdk.Tx tx sdk.Tx
}{ }{
{NewTxUnbond(strconv.Itoa(bondAmt), pubKey)}, {NewMsgUnbond(strconv.Itoa(bondAmt), pubKey)},
{NewTxDeclareCandidacy(bond, pubKey, Description{})}, {NewMsgDeclareCandidacy(bond, pubKey, Description{})},
{NewTxDeclareCandidacy(bond, pubKey, Description{})}, {NewMsgDeclareCandidacy(bond, pubKey, Description{})},
// {NewTxRevokeCandidacy(pubKey)}, // {NewTxRevokeCandidacy(pubKey)},
} }

View File

@ -13,8 +13,8 @@ type Params struct {
InflationMin sdk.Rational `json:"inflation_min"` // minimum inflation rate InflationMin sdk.Rational `json:"inflation_min"` // minimum inflation rate
GoalBonded sdk.Rational `json:"goal_bonded"` // Goal of percent bonded atoms GoalBonded sdk.Rational `json:"goal_bonded"` // Goal of percent bonded atoms
MaxVals uint16 `json:"max_vals"` // maximum number of validators MaxVals uint16 `json:"max_vals"` // maximum number of validators
AllowedBondDenom string `json:"allowed_bond_denom"` // bondable coin denomination BondDenom string `json:"bond_denom"` // bondable coin denomination
// gas costs for txs // gas costs for txs
GasDeclareCandidacy int64 `json:"gas_declare_candidacy"` GasDeclareCandidacy int64 `json:"gas_declare_candidacy"`
@ -30,7 +30,7 @@ func defaultParams() Params {
InflationMin: sdk.NewRat(7, 100), InflationMin: sdk.NewRat(7, 100),
GoalBonded: sdk.NewRat(67, 100), GoalBonded: sdk.NewRat(67, 100),
MaxVals: 100, MaxVals: 100,
AllowedBondDenom: "fermion", BondDenom: "fermion",
GasDeclareCandidacy: 20, GasDeclareCandidacy: 20,
GasEditCandidacy: 20, GasEditCandidacy: 20,
GasDelegate: 20, GasDelegate: 20,
@ -92,6 +92,7 @@ func (gs *GlobalState) unbondedShareExRate() sdk.Rational {
// XXX XXX XXX // XXX XXX XXX
// expand to include the function of actually transfering the tokens // expand to include the function of actually transfering the tokens
//XXX CONFIRM that use of the exRate is correct with Zarko Spec!
func (gs *GlobalState) addTokensBonded(amount int64) (issuedShares sdk.Rational) { func (gs *GlobalState) addTokensBonded(amount int64) (issuedShares sdk.Rational) {
issuedShares = gs.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens issuedShares = gs.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
gs.BondedPool += amount gs.BondedPool += amount
@ -99,6 +100,7 @@ func (gs *GlobalState) addTokensBonded(amount int64) (issuedShares sdk.Rational)
return return
} }
//XXX CONFIRM that use of the exRate is correct with Zarko Spec!
func (gs *GlobalState) removeSharesBonded(shares sdk.Rational) (removedTokens int64) { func (gs *GlobalState) removeSharesBonded(shares sdk.Rational) (removedTokens int64) {
removedTokens = gs.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares removedTokens = gs.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.BondedShares = gs.BondedShares.Sub(shares) gs.BondedShares = gs.BondedShares.Sub(shares)
@ -106,6 +108,7 @@ func (gs *GlobalState) removeSharesBonded(shares sdk.Rational) (removedTokens in
return return
} }
//XXX CONFIRM that use of the exRate is correct with Zarko Spec!
func (gs *GlobalState) addTokensUnbonded(amount int64) (issuedShares sdk.Rational) { func (gs *GlobalState) addTokensUnbonded(amount int64) (issuedShares sdk.Rational) {
issuedShares = gs.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens issuedShares = gs.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
gs.UnbondedShares = gs.UnbondedShares.Add(issuedShares) gs.UnbondedShares = gs.UnbondedShares.Add(issuedShares)
@ -113,6 +116,7 @@ func (gs *GlobalState) addTokensUnbonded(amount int64) (issuedShares sdk.Rationa
return return
} }
//XXX CONFIRM that use of the exRate is correct with Zarko Spec!
func (gs *GlobalState) removeSharesUnbonded(shares sdk.Rational) (removedTokens int64) { func (gs *GlobalState) removeSharesUnbonded(shares sdk.Rational) (removedTokens int64) {
removedTokens = gs.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares removedTokens = gs.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.UnbondedShares = gs.UnbondedShares.Sub(shares) gs.UnbondedShares = gs.UnbondedShares.Sub(shares)
@ -144,7 +148,7 @@ const (
type Candidate struct { type Candidate struct {
Status CandidateStatus `json:"status"` // Bonded status Status CandidateStatus `json:"status"` // Bonded status
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
Owner crypto.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here Address sdk.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here
Assets sdk.Rational `json:"assets"` // total shares of a global hold pools TODO custom type PoolShares Assets sdk.Rational `json:"assets"` // total shares of a global hold pools TODO custom type PoolShares
Liabilities sdk.Rational `json:"liabilities"` // total shares issued to a candidate's delegators TODO custom type DelegatorShares Liabilities sdk.Rational `json:"liabilities"` // total shares issued to a candidate's delegators TODO custom type DelegatorShares
VotingPower sdk.Rational `json:"voting_power"` // Voting power if considered a validator VotingPower sdk.Rational `json:"voting_power"` // Voting power if considered a validator
@ -160,11 +164,11 @@ type Description struct {
} }
// NewCandidate - initialize a new candidate // NewCandidate - initialize a new candidate
func NewCandidate(pubKey crypto.PubKey, owner crypto.Address, description Description) *Candidate { func NewCandidate(pubKey crypto.PubKey, address sdk.Address, description Description) *Candidate {
return &Candidate{ return &Candidate{
Status: Unbonded, Status: Unbonded,
PubKey: pubKey, PubKey: pubKet,
Owner: owner, Address: address,
Assets: sdk.ZeroRat, Assets: sdk.ZeroRat,
Liabilities: sdk.ZeroRat, Liabilities: sdk.ZeroRat,
VotingPower: sdk.ZeroRat, VotingPower: sdk.ZeroRat,
@ -172,8 +176,6 @@ func NewCandidate(pubKey crypto.PubKey, owner crypto.Address, description Descri
} }
} }
// XXX define candidate interface?
// get the exchange rate of global pool shares over delegator shares // get the exchange rate of global pool shares over delegator shares
func (c *Candidate) delegatorShareExRate() sdk.Rational { func (c *Candidate) delegatorShareExRate() sdk.Rational {
if c.Liabilities.IsZero() { if c.Liabilities.IsZero() {
@ -254,6 +256,37 @@ type Candidates []*Candidate
// owned by one delegator, and is associated with the voting power of one // owned by one delegator, and is associated with the voting power of one
// pubKey. // pubKey.
type DelegatorBond struct { type DelegatorBond struct {
PubKey crypto.PubKey `json:"pub_key"` Address sdk.Address `json:"pub_key"`
Shares sdk.Rational `json:"shares"` Shares sdk.Rational `json:"shares"`
}
// 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})
if err != nil {
return err
}
newShares = candidate.addTokens(tokens.Amount, tr.gs)
bond.Shares = bond.Shares.Add(newShares)
return nil
}
// Perform all the actions required to bond tokens to a delegator bond from their account
func (bond *DelegatorBond) UnbondCoins(candidate *Candidate, shares int64, tr transact) sdk.Error {
// subtract bond tokens from delegator bond
if bond.Shares.LT(shares) {
return ErrInsufficientFunds()
}
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)
if err != nil {
return err
}
return nil
} }