refactor, staking uses addresses instead of pubkey
staking refactoring working working
This commit is contained in:
parent
292e156872
commit
dc8636390c
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
307
x/stake/tx.go
307
x/stake/tx.go
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue