working sunny staking refactor
This commit is contained in:
parent
c1d8aefecd
commit
93173e9fdd
|
@ -274,7 +274,7 @@ type TxDeclareCandidacy struct {
|
|||
}
|
||||
|
||||
declareCandidacy(tx TxDeclareCandidacy):
|
||||
candidate = loadCandidate(store, tx.PubKey)
|
||||
candidate = getCandidate(store, msg.Address)
|
||||
if candidate != nil return // candidate with that public key already exists
|
||||
|
||||
candidate = NewCandidate(tx.PubKey)
|
||||
|
@ -285,7 +285,7 @@ declareCandidacy(tx TxDeclareCandidacy):
|
|||
candidate.ProposerRewardPool = Coin(0)
|
||||
candidate.Description = tx.Description
|
||||
|
||||
saveCandidate(store, candidate)
|
||||
setCandidate(store, candidate)
|
||||
|
||||
txDelegate = TxDelegate(tx.PubKey, tx.Amount)
|
||||
return delegateWithCandidate(txDelegate, candidate)
|
||||
|
|
|
@ -72,6 +72,9 @@ func ErrBadValidatorAddr() sdk.Error {
|
|||
func ErrBadCandidateAddr() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Candidate does not exist for that address")
|
||||
}
|
||||
func ErrBadDelegatorAddr() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Delegator does not exist for that address")
|
||||
}
|
||||
func ErrCandidateExistsAddr() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
|
||||
}
|
||||
|
|
|
@ -2,93 +2,67 @@ package stake
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
//nolint
|
||||
const (
|
||||
GasDeclareCandidacy int64 = 20
|
||||
GasEditCandidacy int64 = 20
|
||||
GasDelegate int64 = 20
|
||||
GasUnbond int64 = 20
|
||||
)
|
||||
|
||||
//XXX fix initstater
|
||||
// separated for testing
|
||||
func InitState(ctx sdk.Context, mapper Mapper, key, value string) sdk.Error {
|
||||
//func InitState(ctx sdk.Context, k Keeper, key, value string) sdk.Error {
|
||||
|
||||
params := mapper.loadParams()
|
||||
switch key {
|
||||
case "allowed_bond_denom":
|
||||
params.BondDenom = value
|
||||
case "max_vals", "gas_bond", "gas_unbond":
|
||||
//params := k.getParams(ctx)
|
||||
//switch key {
|
||||
//case "allowed_bond_denom":
|
||||
//params.BondDenom = value
|
||||
//case "max_vals", "gas_bond", "gas_unbond":
|
||||
|
||||
i, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return sdk.ErrUnknownRequest(fmt.Sprintf("input must be integer, Error: %v", err.Error()))
|
||||
}
|
||||
//i, err := strconv.Atoi(value)
|
||||
//if err != nil {
|
||||
//return sdk.ErrUnknownRequest(fmt.Sprintf("input must be integer, Error: %v", err.Error()))
|
||||
//}
|
||||
|
||||
switch key {
|
||||
case "max_vals":
|
||||
if i < 0 {
|
||||
return sdk.ErrUnknownRequest("cannot designate negative max validators")
|
||||
}
|
||||
params.MaxVals = uint16(i)
|
||||
case "gas_bond":
|
||||
params.GasDelegate = int64(i)
|
||||
case "gas_unbound":
|
||||
params.GasUnbond = int64(i)
|
||||
}
|
||||
default:
|
||||
return sdk.ErrUnknownRequest(key)
|
||||
}
|
||||
//switch key {
|
||||
//case "max_vals":
|
||||
//if i < 0 {
|
||||
//return sdk.ErrUnknownRequest("cannot designate negative max validators")
|
||||
//}
|
||||
//params.MaxValidators = uint16(i)
|
||||
//case "gas_bond":
|
||||
//GasDelegate = int64(i)
|
||||
//case "gas_unbound":
|
||||
//GasUnbond = int64(i)
|
||||
//}
|
||||
//default:
|
||||
//return sdk.ErrUnknownRequest(key)
|
||||
//}
|
||||
|
||||
mapper.saveParams(params)
|
||||
return nil
|
||||
}
|
||||
//k.setParams(params)
|
||||
//return nil
|
||||
//}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func NewHandler(mapper Mapper, ck bank.CoinKeeper) sdk.Handler {
|
||||
func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
|
||||
params := mapper.loadParams()
|
||||
|
||||
err := msg.ValidateBasic()
|
||||
if err != nil {
|
||||
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, sender, mapper, ck)
|
||||
|
||||
// Run the transaction
|
||||
// NOTE msg already has validate basic run
|
||||
switch msg := msg.(type) {
|
||||
case MsgDeclareCandidacy:
|
||||
res := transact.declareCandidacy(msg).Result()
|
||||
if !ctx.IsCheckTx() {
|
||||
res.GasUsed = params.GasDeclareCandidacy
|
||||
}
|
||||
return res
|
||||
return k.handleMsgDeclareCandidacy(ctx, msg)
|
||||
case MsgEditCandidacy:
|
||||
res := transact.editCandidacy(msg).Result()
|
||||
if !ctx.IsCheckTx() {
|
||||
res.GasUsed = params.GasEditCandidacy
|
||||
}
|
||||
return res
|
||||
return k.handleMsgEditCandidacy(ctx, msg)
|
||||
case MsgDelegate:
|
||||
res := transact.delegate(msg).Result()
|
||||
if !ctx.IsCheckTx() {
|
||||
res.GasUsed = params.GasDelegate
|
||||
}
|
||||
return res
|
||||
return k.handleMsgDelegate(ctx, msg)
|
||||
case MsgUnbond:
|
||||
res := transact.unbond(msg).Result()
|
||||
if !ctx.IsCheckTx() {
|
||||
res.GasUsed = params.GasUnbond
|
||||
}
|
||||
return res
|
||||
return k.handleMsgUnbond(ctx, msg)
|
||||
default:
|
||||
return sdk.ErrTxParse("invalid message parse in staking module").Result()
|
||||
}
|
||||
|
@ -97,249 +71,239 @@ func NewHandler(mapper Mapper, ck bank.CoinKeeper) sdk.Handler {
|
|||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// common fields to all transactions
|
||||
type transact struct {
|
||||
ctx sdk.Context
|
||||
sender crypto.Address
|
||||
mapper Mapper
|
||||
coinKeeper bank.CoinKeeper
|
||||
params Params
|
||||
gs *GlobalState
|
||||
}
|
||||
|
||||
func newTransact(ctx sdk.Context, sender sdk.Address, mapper Mapper, ck bank.CoinKeeper) transact {
|
||||
return transact{
|
||||
ctx: ctx,
|
||||
sender: sender,
|
||||
mapper: mapper,
|
||||
coinKeeper: ck,
|
||||
params: mapper.loadParams(),
|
||||
gs: mapper.loadGlobalState(),
|
||||
}
|
||||
}
|
||||
// XXX should be send in the msg (init in CLI)
|
||||
//func getSender() sdk.Address {
|
||||
//signers := msg.GetSigners()
|
||||
//if len(signers) != 1 {
|
||||
//return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
|
||||
//}
|
||||
//sender := signers[0]
|
||||
//}
|
||||
|
||||
//_____________________________________________________________________
|
||||
// helper functions
|
||||
|
||||
// move a candidates asset pool from bonded to unbonded pool
|
||||
func (tr transact) bondedToUnbondedPool(candidate *Candidate) {
|
||||
func (k Keeper) bondedToUnbondedPool(ctx sdk.Context, candidate *Candidate) {
|
||||
|
||||
// replace bonded shares with unbonded shares
|
||||
tokens := tr.gs.removeSharesBonded(candidate.Assets)
|
||||
candidate.Assets = tr.gs.addTokensUnbonded(tokens)
|
||||
tokens := k.getGlobalState(ctx).removeSharesBonded(candidate.Assets)
|
||||
candidate.Assets = k.getGlobalState(ctx).addTokensUnbonded(tokens)
|
||||
candidate.Status = Unbonded
|
||||
}
|
||||
|
||||
// move a candidates asset pool from unbonded to bonded pool
|
||||
func (tr transact) unbondedToBondedPool(candidate *Candidate) {
|
||||
func (k Keeper) unbondedToBondedPool(ctx sdk.Context, candidate *Candidate) {
|
||||
|
||||
// replace unbonded shares with bonded shares
|
||||
tokens := tr.gs.removeSharesUnbonded(candidate.Assets)
|
||||
candidate.Assets = tr.gs.addTokensBonded(tokens)
|
||||
tokens := k.getGlobalState(ctx).removeSharesUnbonded(candidate.Assets)
|
||||
candidate.Assets = k.getGlobalState(ctx).addTokensBonded(tokens)
|
||||
candidate.Status = Bonded
|
||||
}
|
||||
|
||||
// return an error if the bonds coins are incorrect
|
||||
func checkDenom(mapper Mapper, bond sdk.Coin) sdk.Error {
|
||||
if bond.Denom != mapper.loadParams().BondDenom {
|
||||
return ErrBadBondingDenom()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
func (tr transact) declareCandidacy(tx MsgDeclareCandidacy) sdk.Error {
|
||||
func (k Keeper) handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy) sdk.Result {
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
if tr.mapper.loadCandidate(tx.Address) != nil {
|
||||
if k.getCandidate(msg.Address) != nil {
|
||||
return ErrCandidateExistsAddr()
|
||||
}
|
||||
err := checkDenom(tr.mapper, tx.Bond)
|
||||
if err != nil {
|
||||
return err
|
||||
if msg.bond.Denom != k.getParams().BondDenom {
|
||||
return ErrBadBondingDenom()
|
||||
}
|
||||
if tr.ctx.IsCheckTx() {
|
||||
return nil
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasDeclareCandidacy,
|
||||
}
|
||||
}
|
||||
|
||||
candidate := NewCandidate(tx.PubKey, tr.sender, tx.Description)
|
||||
tr.mapper.saveCandidate(candidate)
|
||||
candidate := NewCandidate(msg.PubKey, msg.Address, msg.Description)
|
||||
k.setCandidate(candidate)
|
||||
|
||||
// move coins from the tr.sender account to a (self-bond) delegator account
|
||||
// move coins from the msg.Address account to a (self-bond) delegator account
|
||||
// the candidate account and global shares are updated within here
|
||||
txDelegate := NewMsgDelegate(tx.Address, tx.Bond)
|
||||
return tr.delegateWithCandidate(txDelegate, candidate)
|
||||
txDelegate := NewMsgDelegate(msg.Address, msg.Bond)
|
||||
return delegateWithCandidate(txDelegate, candidate)
|
||||
}
|
||||
|
||||
func (tr transact) editCandidacy(tx MsgEditCandidacy) sdk.Error {
|
||||
func (k Keeper) handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy) sdk.Result {
|
||||
|
||||
// candidate must already be registered
|
||||
if tr.mapper.loadCandidate(tx.Address) == nil {
|
||||
return ErrBadCandidateAddr()
|
||||
if k.getCandidate(msg.Address) == nil {
|
||||
return ErrBadCandidateAddr().Result()
|
||||
}
|
||||
if tr.ctx.IsCheckTx() {
|
||||
return nil
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasEditCandidacy,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the pubKey bond account
|
||||
candidate := tr.mapper.loadCandidate(tx.Address)
|
||||
candidate := k.getCandidate(msg.Address)
|
||||
if candidate == nil {
|
||||
return ErrBondNotNominated()
|
||||
return ErrBondNotNominated().Result()
|
||||
}
|
||||
if candidate.Status == Unbonded { //candidate has been withdrawn
|
||||
return ErrBondNotNominated()
|
||||
return ErrBondNotNominated().Result()
|
||||
}
|
||||
|
||||
//check and edit any of the editable terms
|
||||
if tx.Description.Moniker != "" {
|
||||
candidate.Description.Moniker = tx.Description.Moniker
|
||||
if msg.Description.Moniker != "" {
|
||||
candidate.Description.Moniker = msg.Description.Moniker
|
||||
}
|
||||
if tx.Description.Identity != "" {
|
||||
candidate.Description.Identity = tx.Description.Identity
|
||||
if msg.Description.Identity != "" {
|
||||
candidate.Description.Identity = msg.Description.Identity
|
||||
}
|
||||
if tx.Description.Website != "" {
|
||||
candidate.Description.Website = tx.Description.Website
|
||||
if msg.Description.Website != "" {
|
||||
candidate.Description.Website = msg.Description.Website
|
||||
}
|
||||
if tx.Description.Details != "" {
|
||||
candidate.Description.Details = tx.Description.Details
|
||||
if msg.Description.Details != "" {
|
||||
candidate.Description.Details = msg.Description.Details
|
||||
}
|
||||
|
||||
tr.mapper.saveCandidate(candidate)
|
||||
k.setCandidate(candidate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr transact) delegate(tx MsgDelegate) sdk.Error {
|
||||
func (k Keeper) handleMsgDelegate(ctx sdk.Context, msg MsgDelegate) sdk.Result {
|
||||
|
||||
if tr.mapper.loadCandidate(tx.Address) == nil {
|
||||
return ErrBadCandidateAddr()
|
||||
if k.getCandidate(msg.Address) == nil {
|
||||
return ErrBadCandidateAddr().Result()
|
||||
}
|
||||
err := checkDenom(tr.mapper, tx.Bond)
|
||||
if err != nil {
|
||||
return err
|
||||
if msg.bond.Denom != k.getParams().BondDenom {
|
||||
return ErrBadBondingDenom().Result()
|
||||
}
|
||||
if tr.ctx.IsCheckTx() {
|
||||
return nil
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasDelegate,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the pubKey bond account
|
||||
candidate := tr.mapper.loadCandidate(tx.Address)
|
||||
candidate := k.getCandidate(msg.Address)
|
||||
if candidate == nil {
|
||||
return ErrBondNotNominated()
|
||||
return ErrBondNotNominated().Result()
|
||||
}
|
||||
return tr.delegateWithCandidate(tx, candidate)
|
||||
|
||||
return tr.delegateWithCandidate(msg, candidate).Result()
|
||||
}
|
||||
|
||||
func (tr transact) delegateWithCandidate(tx MsgDelegate, candidate *Candidate) sdk.Error {
|
||||
func (k Keeper) delegateWithCandidate(ctx sdk.Context, candidateAddr, delegatorAddr sdk.Address,
|
||||
bondAmt sdk.Coin, candidate Candidate) sdk.Error {
|
||||
|
||||
if candidate.Status == Revoked { //candidate has been withdrawn
|
||||
return ErrBondNotNominated()
|
||||
}
|
||||
|
||||
// Get or create the delegator bond
|
||||
bond := tr.mapper.loadDelegatorBond(tr.sender, tx.Address)
|
||||
bond := k.getDelegatorBond(tr.sender, canad)
|
||||
if bond == nil {
|
||||
bond = &DelegatorBond{
|
||||
Address: tx.Address,
|
||||
Shares: sdk.ZeroRat,
|
||||
CandidateAddr: delegatorAddr,
|
||||
DelegatorAddr: candidateAddr,
|
||||
Shares: sdk.ZeroRat,
|
||||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
err := tr.BondCoins(bond, candidate, tx.Bond)
|
||||
err := BondCoins(bond, candidate, msg.Bond)
|
||||
if err != nil {
|
||||
return err
|
||||
return err.Result()
|
||||
}
|
||||
tr.mapper.saveDelegatorBond(tr.sender, bond)
|
||||
tr.mapper.saveCandidate(candidate)
|
||||
tr.mapper.saveGlobalState(tr.gs)
|
||||
k.setDelegatorBond(tr.sender, bond)
|
||||
k.setCandidate(candidate)
|
||||
k.setGlobalState(tr.gs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform all the actions required to bond tokens to a delegator bond from their account
|
||||
func (tr *transact) BondCoins(bond *DelegatorBond, candidate *Candidate, tokens sdk.Coin) sdk.Error {
|
||||
func (k Keeper) BondCoins(ctx sdk.Context, bond DelegatorBond, amount sdk.Coin) sdk.Error {
|
||||
|
||||
_, err := tr.coinKeeper.SubtractCoins(tr.ctx, candidate.Address, sdk.Coins{tokens})
|
||||
_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{amount})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newShares := candidate.addTokens(tokens.Amount, tr.gs)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
k.SetDelegatorBond()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform all the actions required to bond tokens to a delegator bond from their account
|
||||
func (tr *transact) UnbondCoins(bond *DelegatorBond, candidate *Candidate, shares sdk.Rat) sdk.Error {
|
||||
func (k Keeper) UnbondCoins(ctx sdk.Context, bond *DelegatorBond, candidate *Candidate, shares sdk.Rat) sdk.Error {
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
if bond.Shares.LT(shares) {
|
||||
return sdk.ErrInsufficientFunds("") // TODO
|
||||
return sdk.ErrInsufficientFunds("") //XXX variables inside
|
||||
}
|
||||
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, candidate.Address, returnCoins)
|
||||
_, err := tr.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr transact) unbond(tx MsgUnbond) sdk.Error {
|
||||
func (k Keeper) handleMsgUnbond(ctx sdk.Context, msg MsgUnbond) sdk.Result {
|
||||
|
||||
// check if bond has any shares in it unbond
|
||||
bond := tr.mapper.loadDelegatorBond(tr.sender, tx.Address)
|
||||
bond := k.getDelegatorBond(sender, msg.Address)
|
||||
if bond == nil {
|
||||
return ErrNoDelegatorForAddress()
|
||||
return ErrNoDelegatorForAddress().Result()
|
||||
}
|
||||
if !bond.Shares.GT(sdk.ZeroRat) { // bond shares < tx shares
|
||||
return ErrInsufficientFunds()
|
||||
if !bond.Shares.GT(sdk.ZeroRat) { // bond shares < msg shares
|
||||
return ErrInsufficientFunds().Result()
|
||||
}
|
||||
|
||||
// if shares set to special case Max then we're good
|
||||
if tx.Shares != "MAX" {
|
||||
if msg.Shares != "MAX" {
|
||||
// test getting rational number from decimal provided
|
||||
shares, err := sdk.NewRatFromDecimal(tx.Shares)
|
||||
shares, err := sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return err
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// test that there are enough shares to unbond
|
||||
if !bond.Shares.GT(shares) {
|
||||
return ErrNotEnoughBondShares(tx.Shares)
|
||||
return ErrNotEnoughBondShares(msg.Shares).Result()
|
||||
}
|
||||
}
|
||||
if tr.ctx.IsCheckTx() {
|
||||
return nil
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasUnbond,
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
|
||||
var shares sdk.Rat
|
||||
var err sdk.Error
|
||||
if tx.Shares == "MAX" {
|
||||
if msg.Shares == "MAX" {
|
||||
shares = bond.Shares
|
||||
} else {
|
||||
shares, err = sdk.NewRatFromDecimal(tx.Shares)
|
||||
shares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return err
|
||||
return err.Result()
|
||||
}
|
||||
}
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
if bond.Shares.LT(shares) { // bond shares < tx shares
|
||||
return ErrInsufficientFunds()
|
||||
if bond.Shares.LT(shares) { // bond shares < msg shares
|
||||
return ErrInsufficientFunds().Result()
|
||||
}
|
||||
bond.Shares = bond.Shares.Sub(shares)
|
||||
|
||||
// get pubKey candidate
|
||||
candidate := tr.mapper.loadCandidate(tx.Address)
|
||||
candidate := k.getCandidate(msg.Address)
|
||||
if candidate == nil {
|
||||
return ErrNoCandidateForAddress()
|
||||
return ErrNoCandidateForAddress().Result()
|
||||
}
|
||||
|
||||
revokeCandidacy := false
|
||||
|
@ -353,15 +317,15 @@ func (tr transact) unbond(tx MsgUnbond) sdk.Error {
|
|||
}
|
||||
|
||||
// remove the bond
|
||||
tr.mapper.removeDelegatorBond(tr.sender, tx.Address)
|
||||
k.removeDelegatorBond(ctx, msg.Address)
|
||||
} else {
|
||||
tr.mapper.saveDelegatorBond(tr.sender, bond)
|
||||
k.setDelegatorBond(tr.sender, bond)
|
||||
}
|
||||
|
||||
// Add the coins
|
||||
returnAmount := candidate.removeShares(shares, tr.gs)
|
||||
returnCoins := sdk.Coins{{tr.params.BondDenom, returnAmount}}
|
||||
tr.coinKeeper.AddCoins(tr.ctx, tr.sender, returnCoins)
|
||||
tr.coinKeeper.AddCoins(ctx, tr.sender, returnCoins)
|
||||
|
||||
// lastly if an revoke candidate if necessary
|
||||
if revokeCandidacy {
|
||||
|
@ -377,11 +341,11 @@ func (tr transact) unbond(tx MsgUnbond) sdk.Error {
|
|||
|
||||
// deduct shares from the candidate and save
|
||||
if candidate.Liabilities.IsZero() {
|
||||
tr.mapper.removeCandidate(tx.Address)
|
||||
k.removeCandidate(msg.Address)
|
||||
} else {
|
||||
tr.mapper.saveCandidate(candidate)
|
||||
k.setCandidate(candidate)
|
||||
}
|
||||
|
||||
tr.mapper.saveGlobalState(tr.gs)
|
||||
return nil
|
||||
k.setGlobalState(tr.gs)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidates := mapper.loadCandidates()
|
||||
candidates := mapper.getCandidates()
|
||||
expectedBond += bondAmount
|
||||
//expectedSender := initSender - expectedBond
|
||||
gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
|
@ -96,7 +96,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidates := mapper.loadCandidates()
|
||||
candidates := mapper.getCandidates()
|
||||
expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
|
||||
//expectedSender := initSender + (initBond - expectedBond)
|
||||
gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
|
@ -147,7 +147,7 @@ func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
|||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is bonded
|
||||
candidates := mapper.loadCandidates()
|
||||
candidates := mapper.getCandidates()
|
||||
require.Equal(t, i, len(candidates))
|
||||
val := candidates[i]
|
||||
balanceExpd := initSender - 10
|
||||
|
@ -159,17 +159,17 @@ func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
|||
|
||||
// unbond them all
|
||||
for i, addr := range addrs {
|
||||
candidatePre := mapper.loadCandidate(addrs[i])
|
||||
candidatePre := mapper.getCandidate(addrs[i])
|
||||
msgUndelegate := NewMsgUnbond(addrs[i], "10")
|
||||
deliverer.sender = addr
|
||||
got := deliverer.unbond(msgUndelegate)
|
||||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
candidates := mapper.loadCandidates()
|
||||
candidates := mapper.getCandidates()
|
||||
assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
|
||||
|
||||
candidatePost := mapper.loadCandidate(addrs[i])
|
||||
candidatePost := mapper.getCandidate(addrs[i])
|
||||
balanceExpd := initSender
|
||||
balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
|
||||
assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
|
||||
|
@ -194,7 +194,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is bonded
|
||||
bond := mapper.loadDelegatorBond(delegator, sender)
|
||||
bond := mapper.getDelegatorBond(delegator, sender)
|
||||
assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||
}
|
||||
|
||||
|
@ -206,7 +206,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
bond := mapper.loadDelegatorBond(delegator, sender)
|
||||
bond := mapper.getDelegatorBond(delegator, sender)
|
||||
assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
CandidatesAddrKey = []byte{0x01} // key for all candidates' addresses
|
||||
ParamKey = []byte{0x02} // key for global parameters relating to staking
|
||||
GlobalStateKey = []byte{0x03} // key for global parameters relating to staking
|
||||
|
||||
// Key prefixes
|
||||
CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate
|
||||
ValidatorKeyPrefix = []byte{0x05} // prefix for each key to a candidate
|
||||
ValidatorUpdatesKeyPrefix = []byte{0x06} // prefix for each key to a candidate
|
||||
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
|
||||
DelegatorBondsKeyPrefix = []byte{0x08} // prefix for each key to a delegator's bond
|
||||
)
|
||||
|
||||
// XXX remove beggining word get from all these keys
|
||||
// GetCandidateKey - get the key for the candidate with address
|
||||
func GetCandidateKey(addr sdk.Address) []byte {
|
||||
return append(CandidateKeyPrefix, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// GetValidatorKey - get the key for the validator used in the power-store
|
||||
func GetValidatorKey(addr sdk.Address, power sdk.Rational, cdc *wire.Codec) []byte {
|
||||
b, _ := cdc.MarshalJSON(power) // TODO need to handle error here?
|
||||
return append(ValidatorKeyPrefix, append(b, addr.Bytes()...)...) // TODO does this need prefix if its in its own store
|
||||
}
|
||||
|
||||
// GetValidatorUpdatesKey - get the key for the validator used in the power-store
|
||||
func GetValidatorUpdatesKey(addr sdk.Address) []byte {
|
||||
return append(ValidatorUpdatesKeyPrefix, addr.Bytes()...) // TODO does this need prefix if its in its own store
|
||||
}
|
||||
|
||||
// GetDelegatorBondKey - get the key for delegator bond with candidate
|
||||
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegatorBondKeyPrefix(delegatorAddr, cdc), candidateAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
|
||||
func GetDelegatorBondKeyPrefix(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalJSON(&delegatorAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegatorBondKeyPrefix, res...)
|
||||
}
|
||||
|
||||
// GetDelegatorBondsKey - get the key for list of all the delegator's bonds
|
||||
func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalJSON(&delegatorAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegatorBondsKeyPrefix, res...)
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// keeper of the staking store
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.CoinKeeper
|
||||
|
||||
//just caches
|
||||
gs GlobalState
|
||||
params Params
|
||||
}
|
||||
|
||||
func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper) Keeper {
|
||||
keeper := Keeper{
|
||||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
//XXX load/save -> get/set
|
||||
func (m Keeper) getCandidate(ctx sdk.Context, addr sdk.Address) (candidate Candidate) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
b := store.Get(GetCandidateKey(addr))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
err := m.cdc.UnmarshalJSON(b, &candidate)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
// XXX should only remove validator if we know candidate is a validator
|
||||
m.removeValidator(candidate.Address)
|
||||
validator := &Validator{candidate.Address, candidate.VotingPower}
|
||||
m.updateValidator(validator)
|
||||
|
||||
b, err := m.cdc.MarshalJSON(candidate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GetCandidateKey(candidate.Address), b)
|
||||
}
|
||||
|
||||
func (m Keeper) removeCandidate(ctx sdk.Context, candidateAddr sdk.Address) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
// XXX should only remove validator if we know candidate is a validator
|
||||
m.removeValidator(candidateAddr)
|
||||
store.Delete(GetCandidateKey(candidateAddr))
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
//func loadValidator(store sdk.KVStore, address sdk.Address, votingPower sdk.Rational) *Validator {
|
||||
//b := store.Get(GetValidatorKey(address, votingPower))
|
||||
//if b == nil {
|
||||
//return nil
|
||||
//}
|
||||
//validator := new(Validator)
|
||||
//err := cdc.UnmarshalJSON(b, validator)
|
||||
//if err != nil {
|
||||
//panic(err) // This error should never occur big problem if does
|
||||
//}
|
||||
//return validator
|
||||
//}
|
||||
|
||||
// updateValidator - update a validator and create accumulate any changes
|
||||
// in the changed validator substore
|
||||
func (m Keeper) updateValidator(ctx sdk.Context, validator Validator) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
b, err := m.cdc.MarshalJSON(validator)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// add to the validators to update list if necessary
|
||||
store.Set(GetValidatorUpdatesKey(validator.Address), b)
|
||||
|
||||
// update the list ordered by voting power
|
||||
store.Set(GetValidatorKey(validator.Address, validator.VotingPower, m.cdc), b)
|
||||
}
|
||||
|
||||
func (m Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
//add validator with zero power to the validator updates
|
||||
b, err := m.cdc.MarshalJSON(Validator{address, sdk.ZeroRat})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GetValidatorUpdatesKey(address), b)
|
||||
|
||||
// now actually delete from the validator set
|
||||
candidate := m.getCandidate(address)
|
||||
if candidate != nil {
|
||||
store.Delete(GetValidatorKey(address, candidate.VotingPower, m.cdc))
|
||||
}
|
||||
}
|
||||
|
||||
// get the most recent updated validator set from the Candidates. These bonds
|
||||
// are already sorted by VotingPower from the UpdateVotingPower function which
|
||||
// is the only function which is to modify the VotingPower
|
||||
func (m Keeper) getValidators(ctx sdk.Context, maxVal uint16) (validators []Validator) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
iterator := store.Iterator(subspace(ValidatorKeyPrefix)) //smallest to largest
|
||||
|
||||
validators = make([]Validator, maxVal)
|
||||
for i := 0; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxVal) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
valBytes := iterator.Value()
|
||||
var val Validator
|
||||
err := m.cdc.UnmarshalJSON(valBytes, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
validators[i] = val
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// get the most updated validators
|
||||
func (m Keeper) getValidatorUpdates(ctx sdk.Context) (updates []Validator) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
iterator := store.Iterator(subspace(ValidatorUpdatesKeyPrefix)) //smallest to largest
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val Validator
|
||||
err := m.cdc.UnmarshalJSON(valBytes, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
updates = append(updates, val)
|
||||
}
|
||||
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries
|
||||
func (m Keeper) clearValidatorUpdates(ctx sdk.Context, maxVal int) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
iterator := store.Iterator(subspace(ValidatorUpdatesKeyPrefix))
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key()) // XXX write test for this, may need to be in a second loop
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// getCandidates - get the active list of all candidates TODO replace with multistore
|
||||
func (m Keeper) getCandidates(ctx sdk.Context) (candidates Candidates) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
iterator := store.Iterator(subspace(CandidateKeyPrefix))
|
||||
//iterator := store.Iterator(CandidateKeyPrefix, []byte(nil))
|
||||
//iterator := store.Iterator([]byte{}, []byte(nil))
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
candidateBytes := iterator.Value()
|
||||
var candidate Candidate
|
||||
err := m.cdc.UnmarshalJSON(candidateBytes, &candidate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
candidates = append(candidates, &candidate)
|
||||
}
|
||||
iterator.Close()
|
||||
return candidates
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// XXX use a store iterator to get
|
||||
//// load the pubkeys of all candidates a delegator is delegated too
|
||||
//func (m Keeper) getDelegatorCandidates(ctx sdk.Context, delegator sdk.Address) (candidateAddrs []sdk.Address) {
|
||||
//store := ctx.KVStore(storeKey)
|
||||
|
||||
//candidateBytes := store.Get(GetDelegatorBondsKey(delegator, m.cdc))
|
||||
//if candidateBytes == nil {
|
||||
//return nil
|
||||
//}
|
||||
|
||||
//err := m.cdc.UnmarshalJSON(candidateBytes, &candidateAddrs)
|
||||
//if err != nil {
|
||||
//panic(err)
|
||||
//}
|
||||
//return
|
||||
//}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
func (m Keeper) getDelegatorBond(ctx sdk.Context,
|
||||
delegator, candidate sdk.Address) (bond DelegatorBond) {
|
||||
|
||||
store := ctx.KVStore(storeKey)
|
||||
delegatorBytes := store.Get(GetDelegatorBondKey(delegator, candidate, m.cdc))
|
||||
if delegatorBytes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := m.cdc.UnmarshalJSON(delegatorBytes, &bond)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
func (m Keeper) setDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
// XXX use store iterator
|
||||
// if a new bond add to the list of bonds
|
||||
//if m.getDelegatorBond(delegator, bond.Address) == nil {
|
||||
//pks := m.getDelegatorCandidates(delegator)
|
||||
//pks = append(pks, bond.Address)
|
||||
//b, err := m.cdc.MarshalJSON(pks)
|
||||
//if err != nil {
|
||||
//panic(err)
|
||||
//}
|
||||
//store.Set(GetDelegatorBondsKey(delegator, m.cdc), b)
|
||||
//}
|
||||
|
||||
// now actually save the bond
|
||||
b, err := m.cdc.MarshalJSON(bond)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GetDelegatorBondKey(delegator, bond.Address, m.cdc), b)
|
||||
}
|
||||
|
||||
func (m Keeper) removeDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
|
||||
// XXX use store iterator
|
||||
// TODO use list queries on multistore to remove iterations here!
|
||||
// first remove from the list of bonds
|
||||
//addrs := m.getDelegatorCandidates(delegator)
|
||||
//for i, addr := range addrs {
|
||||
//if bytes.Equal(candidateAddr, addr) {
|
||||
//addrs = append(addrs[:i], addrs[i+1:]...)
|
||||
//}
|
||||
//}
|
||||
//b, err := m.cdc.MarshalJSON(addrs)
|
||||
//if err != nil {
|
||||
//panic(err)
|
||||
//}
|
||||
//store.Set(GetDelegatorBondsKey(delegator, m.cdc), b)
|
||||
|
||||
// now remove the actual bond
|
||||
store.Delete(GetDelegatorBondKey(bond.delegatorAddr, bond.candidateAddr, m.cdc))
|
||||
//updateDelegatorBonds(store, delegator) //XXX remove?
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the global staking params
|
||||
func (m Keeper) getParams(ctx sdk.Context) (params Params) {
|
||||
// check if cached before anything
|
||||
if m.params != (Params{}) {
|
||||
return m.params
|
||||
}
|
||||
store := ctx.KVStore(storeKey)
|
||||
b := store.Get(ParamKey)
|
||||
if b == nil {
|
||||
return defaultParams()
|
||||
}
|
||||
|
||||
err := m.cdc.UnmarshalJSON(b, ¶ms)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
func (m Keeper) setParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
b, err := m.cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(ParamKey, b)
|
||||
m.params = Params{} // clear the cache
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// XXX nothing is this Keeper should return a pointer...!!!!!!
|
||||
// load/save the global staking state
|
||||
func (m Keeper) getGlobalState(ctx sdk.Context) (gs GlobalState) {
|
||||
// check if cached before anything
|
||||
if m.gs != nil {
|
||||
return m.gs
|
||||
}
|
||||
store := ctx.KVStore(storeKey)
|
||||
b := store.Get(GlobalStateKey)
|
||||
if b == nil {
|
||||
return initialGlobalState()
|
||||
}
|
||||
gs = new(GlobalState)
|
||||
err := m.cdc.UnmarshalJSON(b, &gs)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m Keeper) setGlobalState(ctx sdk.Context, gs GlobalState) {
|
||||
store := ctx.KVStore(storeKey)
|
||||
b, err := m.cdc.MarshalJSON(gs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GlobalStateKey, b)
|
||||
m.gs = GlobalState{} // clear the cache
|
||||
}
|
|
@ -18,8 +18,8 @@ import (
|
|||
//func TestUpdateVotingPower(t *testing.T) {
|
||||
//assert := assert.New(t)
|
||||
//store := initTestStore(t)
|
||||
//params := loadParams(store)
|
||||
//gs := loadGlobalState(store)
|
||||
//params := getParams(store)
|
||||
//gs := getGlobalState(store)
|
||||
|
||||
//N := 5
|
||||
//actors := newAddrs(N)
|
||||
|
@ -38,7 +38,7 @@ import (
|
|||
|
||||
//// test the max validators term
|
||||
//params.MaxVals = 4
|
||||
//saveParams(store, params)
|
||||
//setParams(store, params)
|
||||
//candidates.updateVotingPower(store, gs, params)
|
||||
//assert.Equal(int64(0), candidates[4].VotingPower.Evaluate(), "%v", candidates[4])
|
||||
//}
|
||||
|
@ -118,11 +118,11 @@ import (
|
|||
//testRemove(t, vs1[1], changed[0])
|
||||
//testRemove(t, vs1[2], changed[1])
|
||||
|
||||
//// test many sdk of changes //vs2 = []Validator{v1, v3, v4, v5} //vs2[2].VotingPower = sdk.NewRat(11) //changed = vs1.validatorsUpdated(vs2) //require.Equal(4, len(changed), "%v", changed) // change 1, remove 1, add 2 //testRemove(t, vs1[1], changed[0]) //testChange(t, vs2[1], changed[1]) //testChange(t, vs2[2], changed[2]) //testChange(t, vs2[3], changed[3]) //} //func TestUpdateValidatorSet(t *testing.T) { //assert, require := assert.New(t), require.New(t) //store := initTestStore(t) //params := loadParams(store) //gs := loadGlobalState(store) //N := 5
|
||||
//// test many sdk of changes //vs2 = []Validator{v1, v3, v4, v5} //vs2[2].VotingPower = sdk.NewRat(11) //changed = vs1.validatorsUpdated(vs2) //require.Equal(4, len(changed), "%v", changed) // change 1, remove 1, add 2 //testRemove(t, vs1[1], changed[0]) //testChange(t, vs2[1], changed[1]) //testChange(t, vs2[2], changed[2]) //testChange(t, vs2[3], changed[3]) //} //func TestUpdateValidatorSet(t *testing.T) { //assert, require := assert.New(t), require.New(t) //store := initTestStore(t) //params := getParams(store) //gs := getGlobalState(store) //N := 5
|
||||
//actors := newAddrs(N)
|
||||
//candidates := candidatesFromActors(actors, []int64{400, 200, 100, 10, 1})
|
||||
//for _, c := range candidates {
|
||||
//saveCandidate(store, c)
|
||||
//setCandidate(store, c)
|
||||
//}
|
||||
|
||||
//// they should all already be validators
|
||||
|
@ -132,12 +132,12 @@ import (
|
|||
|
||||
//// test the max value and test again
|
||||
//params.MaxVals = 4
|
||||
//saveParams(store, params)
|
||||
//setParams(store, params)
|
||||
//change, err = UpdateValidatorSet(store, gs, params)
|
||||
//require.Nil(err)
|
||||
//require.Equal(1, len(change), "%v", change)
|
||||
//testRemove(t, candidates[4].validator(), change[0])
|
||||
//candidates = loadCandidates(store)
|
||||
//candidates = getCandidates(store)
|
||||
//assert.Equal(int64(0), candidates[4].VotingPower.Evaluate())
|
||||
|
||||
//// mess with the power's of the candidates and test
|
||||
|
@ -147,12 +147,12 @@ import (
|
|||
//candidates[3].Assets = sdk.OneRat
|
||||
//candidates[4].Assets = sdk.NewRat(10)
|
||||
//for _, c := range candidates {
|
||||
//saveCandidate(store, c)
|
||||
//setCandidate(store, c)
|
||||
//}
|
||||
//change, err = UpdateValidatorSet(store, gs, params)
|
||||
//require.Nil(err)
|
||||
//require.Equal(5, len(change), "%v", change) // 3 changed, 1 added, 1 removed
|
||||
//candidates = loadCandidates(store)
|
||||
//candidates = getCandidates(store)
|
||||
//testChange(t, candidates[0].validator(), change[0])
|
||||
//testChange(t, candidates[1].validator(), change[1])
|
||||
//testChange(t, candidates[2].validator(), change[2])
|
||||
|
@ -161,7 +161,7 @@ import (
|
|||
//}
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
_, _, mapper, _ := createTestInput(t, nil, false, 0)
|
||||
_, _, keeper, _ := createTestInput(t, nil, false, 0)
|
||||
|
||||
addrDel := sdk.Address([]byte("addressdelegator"))
|
||||
addrVal := sdk.Address([]byte("addressvalidator"))
|
||||
|
@ -190,26 +190,26 @@ func TestState(t *testing.T) {
|
|||
c1.Description == c2.Description
|
||||
}
|
||||
|
||||
// check the empty mapper first
|
||||
resCand := mapper.loadCandidate(addrVal)
|
||||
// check the empty keeper first
|
||||
resCand := keeper.getCandidate(addrVal)
|
||||
assert.Nil(t, resCand)
|
||||
resPks := mapper.loadCandidates()
|
||||
resPks := keeper.getCandidates()
|
||||
assert.Zero(t, len(resPks))
|
||||
|
||||
// set and retrieve a record
|
||||
mapper.saveCandidate(candidate)
|
||||
resCand = mapper.loadCandidate(addrVal)
|
||||
keeper.setCandidate(candidate)
|
||||
resCand = keeper.getCandidate(addrVal)
|
||||
//assert.Equal(candidate, resCand)
|
||||
assert.True(t, candidatesEqual(candidate, resCand), "%#v \n %#v", resCand, candidate)
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
candidate.Liabilities = sdk.NewRat(99)
|
||||
mapper.saveCandidate(candidate)
|
||||
resCand = mapper.loadCandidate(addrVal)
|
||||
keeper.setCandidate(candidate)
|
||||
resCand = keeper.getCandidate(addrVal)
|
||||
assert.True(t, candidatesEqual(candidate, resCand))
|
||||
|
||||
// also test that the pubkey has been added to pubkey list
|
||||
resPks = mapper.loadCandidates()
|
||||
resPks = keeper.getCandidates()
|
||||
require.Equal(t, 1, len(resPks))
|
||||
assert.Equal(t, addrVal, resPks[0].PubKey)
|
||||
|
||||
|
@ -226,19 +226,19 @@ func TestState(t *testing.T) {
|
|||
b1.Shares == b2.Shares
|
||||
}
|
||||
|
||||
//check the empty mapper first
|
||||
resBond := mapper.loadDelegatorBond(addrDel, addrVal)
|
||||
//check the empty keeper first
|
||||
resBond := keeper.getDelegatorBond(addrDel, addrVal)
|
||||
assert.Nil(t, resBond)
|
||||
|
||||
//Set and retrieve a record
|
||||
mapper.saveDelegatorBond(addrDel, bond)
|
||||
resBond = mapper.loadDelegatorBond(addrDel, addrVal)
|
||||
keeper.setDelegatorBond(addrDel, bond)
|
||||
resBond = keeper.getDelegatorBond(addrDel, addrVal)
|
||||
assert.True(t, bondsEqual(bond, resBond))
|
||||
|
||||
//modify a records, save, and retrieve
|
||||
bond.Shares = sdk.NewRat(99)
|
||||
mapper.saveDelegatorBond(addrDel, bond)
|
||||
resBond = mapper.loadDelegatorBond(addrDel, addrVal)
|
||||
keeper.setDelegatorBond(addrDel, bond)
|
||||
resBond = keeper.getDelegatorBond(addrDel, addrVal)
|
||||
assert.True(t, bondsEqual(bond, resBond))
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -246,22 +246,22 @@ func TestState(t *testing.T) {
|
|||
|
||||
params := defaultParams()
|
||||
|
||||
//check that the empty mapper loads the default
|
||||
resParams := mapper.loadParams()
|
||||
//check that the empty keeper loads the default
|
||||
resParams := keeper.getParams()
|
||||
assert.Equal(t, params, resParams)
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
params.MaxVals = 777
|
||||
mapper.saveParams(params)
|
||||
resParams = mapper.loadParams()
|
||||
keeper.setParams(params)
|
||||
resParams = keeper.getParams()
|
||||
assert.Equal(t, params, resParams)
|
||||
}
|
||||
|
||||
func TestGetValidators(t *testing.T) {
|
||||
_, _, mapper, _ := createTestInput(t, nil, false, 0)
|
||||
candidatesFromAddrs(mapper, addrs, []int64{400, 200, 0, 0, 0})
|
||||
_, _, keeper, _ := createTestInput(t, nil, false, 0)
|
||||
candidatesFromAddrs(keeper, addrs, []int64{400, 200, 0, 0, 0})
|
||||
|
||||
validators := mapper.getValidators(5)
|
||||
validators := keeper.getValidators(5)
|
||||
require.Equal(t, 2, len(validators))
|
||||
assert.Equal(t, addrs[0], validators[0].Address)
|
||||
assert.Equal(t, addrs[1], validators[1].Address)
|
|
@ -1,360 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
CandidatesAddrKey = []byte{0x01} // key for all candidates' addresses
|
||||
ParamKey = []byte{0x02} // key for global parameters relating to staking
|
||||
GlobalStateKey = []byte{0x03} // key for global parameters relating to staking
|
||||
|
||||
// Key prefixes
|
||||
CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate
|
||||
ValidatorKeyPrefix = []byte{0x05} // prefix for each key to a candidate
|
||||
ValidatorUpdatesKeyPrefix = []byte{0x06} // prefix for each key to a candidate
|
||||
DelegatorBondKeyPrefix = []byte{0x07} // 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 address
|
||||
func GetCandidateKey(address sdk.Address) []byte {
|
||||
return append(CandidateKeyPrefix, address.Bytes()...)
|
||||
}
|
||||
|
||||
// GetValidatorKey - get the key for the validator used in the power-store
|
||||
func GetValidatorKey(address sdk.Address, power sdk.Rational, cdc *wire.Codec) []byte {
|
||||
b, _ := cdc.MarshalJSON(power) // TODO need to handle error here?
|
||||
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
|
||||
func GetValidatorUpdatesKey(address sdk.Address) []byte {
|
||||
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
|
||||
func GetDelegatorBondKey(delegator, candidate sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegatorBondKeyPrefix(delegator, cdc), candidate.Bytes()...)
|
||||
}
|
||||
|
||||
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
|
||||
func GetDelegatorBondKeyPrefix(delegator sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalJSON(&delegator)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegatorBondKeyPrefix, res...)
|
||||
}
|
||||
|
||||
// GetDelegatorBondsKey - get the key for list of all the delegator's bonds
|
||||
func GetDelegatorBondsKey(delegator sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalJSON(&delegator)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegatorBondsKeyPrefix, res...)
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// mapper of the staking store
|
||||
type Mapper struct {
|
||||
store sdk.KVStore
|
||||
cdc *wire.Codec
|
||||
}
|
||||
|
||||
func NewMapper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey) Mapper {
|
||||
return Mapper{
|
||||
store: ctx.KVStore(key),
|
||||
cdc: cdc,
|
||||
}
|
||||
}
|
||||
|
||||
func (m Mapper) loadCandidate(address sdk.Address) *Candidate {
|
||||
b := m.store.Get(GetCandidateKey(address))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
candidate := new(Candidate)
|
||||
err := m.cdc.UnmarshalJSON(b, candidate)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
func (m Mapper) saveCandidate(candidate *Candidate) {
|
||||
|
||||
// XXX should only remove validator if we know candidate is a validator
|
||||
m.removeValidator(candidate.Address)
|
||||
validator := &Validator{candidate.Address, candidate.VotingPower}
|
||||
m.updateValidator(validator)
|
||||
|
||||
b, err := m.cdc.MarshalJSON(*candidate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GetCandidateKey(candidate.Address), b)
|
||||
}
|
||||
|
||||
func (m Mapper) removeCandidate(address sdk.Address) {
|
||||
|
||||
// XXX should only remove validator if we know candidate is a validator
|
||||
m.removeValidator(address)
|
||||
m.store.Delete(GetCandidateKey(address))
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
//func loadValidator(m.store sdk.KVStore, address sdk.Address, votingPower sdk.Rational) *Validator {
|
||||
//b := m.store.Get(GetValidatorKey(address, votingPower))
|
||||
//if b == nil {
|
||||
//return nil
|
||||
//}
|
||||
//validator := new(Validator)
|
||||
//err := cdc.UnmarshalJSON(b, validator)
|
||||
//if err != nil {
|
||||
//panic(err) // This error should never occur big problem if does
|
||||
//}
|
||||
//return validator
|
||||
//}
|
||||
|
||||
// updateValidator - update a validator and create accumulate any changes
|
||||
// in the changed validator substore
|
||||
func (m Mapper) updateValidator(validator *Validator) {
|
||||
|
||||
b, err := m.cdc.MarshalJSON(*validator)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// add to the validators to update list if necessary
|
||||
m.store.Set(GetValidatorUpdatesKey(validator.Address), b)
|
||||
|
||||
// update the list ordered by voting power
|
||||
m.store.Set(GetValidatorKey(validator.Address, validator.VotingPower, m.cdc), b)
|
||||
}
|
||||
|
||||
func (m Mapper) removeValidator(address sdk.Address) {
|
||||
|
||||
//add validator with zero power to the validator updates
|
||||
b, err := m.cdc.MarshalJSON(Validator{address, sdk.ZeroRat})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GetValidatorUpdatesKey(address), b)
|
||||
|
||||
// now actually delete from the validator set
|
||||
candidate := m.loadCandidate(address)
|
||||
if candidate != nil {
|
||||
m.store.Delete(GetValidatorKey(address, candidate.VotingPower, m.cdc))
|
||||
}
|
||||
}
|
||||
|
||||
// get the most recent updated validator set from the Candidates. These bonds
|
||||
// are already sorted by VotingPower from the UpdateVotingPower function which
|
||||
// is the only function which is to modify the VotingPower
|
||||
func (m Mapper) getValidators(maxVal uint16) (validators []Validator) {
|
||||
|
||||
iterator := m.store.Iterator(subspace(ValidatorKeyPrefix)) //smallest to largest
|
||||
|
||||
validators = make([]Validator, maxVal)
|
||||
for i := 0; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxVal) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
valBytes := iterator.Value()
|
||||
var val Validator
|
||||
err := m.cdc.UnmarshalJSON(valBytes, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
validators[i] = val
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// get the most updated validators
|
||||
func (m Mapper) getValidatorUpdates() (updates []Validator) {
|
||||
|
||||
iterator := m.store.Iterator(subspace(ValidatorUpdatesKeyPrefix)) //smallest to largest
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val Validator
|
||||
err := m.cdc.UnmarshalJSON(valBytes, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
updates = append(updates, val)
|
||||
}
|
||||
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries
|
||||
func (m Mapper) clearValidatorUpdates(maxVal int) {
|
||||
iterator := m.store.Iterator(subspace(ValidatorUpdatesKeyPrefix))
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
m.store.Delete(iterator.Key()) // XXX write test for this, may need to be in a second loop
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// loadCandidates - get the active list of all candidates TODO replace with multistore
|
||||
func (m Mapper) loadCandidates() (candidates Candidates) {
|
||||
|
||||
iterator := m.store.Iterator(subspace(CandidateKeyPrefix))
|
||||
//iterator := m.store.Iterator(CandidateKeyPrefix, []byte(nil))
|
||||
//iterator := m.store.Iterator([]byte{}, []byte(nil))
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
candidateBytes := iterator.Value()
|
||||
var candidate Candidate
|
||||
err := m.cdc.UnmarshalJSON(candidateBytes, &candidate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
candidates = append(candidates, &candidate)
|
||||
}
|
||||
iterator.Close()
|
||||
return candidates
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// load the pubkeys of all candidates a delegator is delegated too
|
||||
func (m Mapper) loadDelegatorCandidates(delegator sdk.Address) (candidateAddrs []sdk.Address) {
|
||||
|
||||
candidateBytes := m.store.Get(GetDelegatorBondsKey(delegator, m.cdc))
|
||||
if candidateBytes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := m.cdc.UnmarshalJSON(candidateBytes, &candidateAddrs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
func (m Mapper) loadDelegatorBond(delegator, candidate sdk.Address) *DelegatorBond {
|
||||
|
||||
delegatorBytes := m.store.Get(GetDelegatorBondKey(delegator, candidate, m.cdc))
|
||||
if delegatorBytes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bond := new(DelegatorBond)
|
||||
err := m.cdc.UnmarshalJSON(delegatorBytes, bond)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
func (m Mapper) saveDelegatorBond(delegator sdk.Address,
|
||||
bond *DelegatorBond) {
|
||||
|
||||
// if a new bond add to the list of bonds
|
||||
if m.loadDelegatorBond(delegator, bond.Address) == nil {
|
||||
pks := m.loadDelegatorCandidates(delegator)
|
||||
pks = append(pks, (*bond).Address)
|
||||
b, err := m.cdc.MarshalJSON(pks)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GetDelegatorBondsKey(delegator, m.cdc), b)
|
||||
}
|
||||
|
||||
// now actually save the bond
|
||||
b, err := m.cdc.MarshalJSON(*bond)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GetDelegatorBondKey(delegator, bond.Address, m.cdc), b)
|
||||
//updateDelegatorBonds(store, delegator) //XXX remove?
|
||||
}
|
||||
|
||||
func (m Mapper) removeDelegatorBond(delegator sdk.Address, candidateAddr sdk.Address) {
|
||||
// TODO use list queries on multistore to remove iterations here!
|
||||
// first remove from the list of bonds
|
||||
addrs := m.loadDelegatorCandidates(delegator)
|
||||
for i, addr := range addrs {
|
||||
if bytes.Equal(candidateAddr, addr) {
|
||||
addrs = append(addrs[:i], addrs[i+1:]...)
|
||||
}
|
||||
}
|
||||
b, err := m.cdc.MarshalJSON(addrs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GetDelegatorBondsKey(delegator, m.cdc), b)
|
||||
|
||||
// now remove the actual bond
|
||||
m.store.Delete(GetDelegatorBondKey(delegator, candidateAddr, m.cdc))
|
||||
//updateDelegatorBonds(store, delegator) //XXX remove?
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the global staking params
|
||||
func (m Mapper) loadParams() (params Params) {
|
||||
b := m.store.Get(ParamKey)
|
||||
if b == nil {
|
||||
return defaultParams()
|
||||
}
|
||||
|
||||
err := m.cdc.UnmarshalJSON(b, ¶ms)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
func (m Mapper) saveParams(params Params) {
|
||||
b, err := m.cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the global staking state
|
||||
func (m Mapper) loadGlobalState() (gs *GlobalState) {
|
||||
b := m.store.Get(GlobalStateKey)
|
||||
if b == nil {
|
||||
return initialGlobalState()
|
||||
}
|
||||
gs = new(GlobalState)
|
||||
err := m.cdc.UnmarshalJSON(b, gs)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m Mapper) saveGlobalState(gs *GlobalState) {
|
||||
b, err := m.cdc.MarshalJSON(*gs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.store.Set(GlobalStateKey, b)
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// name to idetify transaction types
|
||||
var MsgType = "stake"
|
||||
|
||||
//Verify interface at compile time
|
||||
var _, _, _, _ sdk.Msg = &MsgDeclareCandidacy{}, &MsgEditCandidacy{}, &MsgDelegate{}, &MsgUnbond{}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDeclareCandidacy - struct for unbonding transactions
|
||||
type MsgDeclareCandidacy struct {
|
||||
Description
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
}
|
||||
|
||||
func NewMsgDeclareCandidacy(candidateAddr sdk.Address, pubkey crypto.PubKey,
|
||||
bond sdk.Coin, description Description) MsgDeclareCandidacy {
|
||||
return MsgDeclareCandidacy{
|
||||
Description: description,
|
||||
CandidateAddr: candidateAddr,
|
||||
Bond: bond,
|
||||
PubKey: pubkey,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgDeclareCandidacy) Type() string { return MsgType } //TODO update "stake/declarecandidacy"
|
||||
func (msg MsgDeclareCandidacy) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgDeclareCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.CandidateAddr} }
|
||||
func (msg MsgDeclareCandidacy) String() string {
|
||||
return fmt.Sprintf("CandidateAddr{Address: %v}", msg.Address) // XXX fix
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if msg.Address == nil {
|
||||
return ErrCandidateEmpty()
|
||||
}
|
||||
if msg.Bond.Denom <= 0 {
|
||||
return sdk.ErrInvalidCoins(coins)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(CodeInvalidInput, "description must be included")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgEditCandidacy - struct for editing a candidate
|
||||
type MsgEditCandidacy struct {
|
||||
Description
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
}
|
||||
|
||||
func NewMsgEditCandidacy(address sdk.Address, description Description) MsgEditCandidacy {
|
||||
return MsgEditCandidacy{
|
||||
Description: description,
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgEditCandidacy) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy"
|
||||
func (msg MsgEditCandidacy) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgEditCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.CandidateAddr} }
|
||||
func (msg MsgEditCandidacy) String() string {
|
||||
return fmt.Sprintf("CandidateAddr{Address: %v}", msg.Address) // XXX fix
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if msg.Address == nil {
|
||||
return ErrCandidateEmpty()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(CodeInvalidInput, "Transaction must include some information to modify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"address"`
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDelegate(delegatorAddr, candidateAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgDelegate) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy"
|
||||
func (msg MsgDelegate) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgDelegate) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
func (msg MsgDelegate) String() string {
|
||||
return fmt.Sprintf("Addr{Address: %v}", msg.DelegatorAddr) // XXX fix
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr()
|
||||
}
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrBadCandidateAddr()
|
||||
}
|
||||
if msg.Bond.Denom <= 0 {
|
||||
return sdk.ErrInvalidCoins(coins)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgUnbond - struct for unbonding transactions
|
||||
type MsgUnbond struct {
|
||||
DelegatorAddr sdk.Address `json:"address"`
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
func NewMsgUnbond(delegatorAddr, candidateAddr sdk.Address, shares string) MsgUnbond {
|
||||
return MsgUnbond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
Shares: shares,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgUnbond) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy"
|
||||
func (msg MsgUnbond) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
func (msg MsgUnbond) String() string {
|
||||
return fmt.Sprintf("Addr{Address: %v}", msg.DelegatorAddr) // XXX fix
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr()
|
||||
}
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrBadCandidateAddr()
|
||||
}
|
||||
if msg.Shares != "MAX" {
|
||||
shares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return ErrBadShares()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -96,7 +96,7 @@ func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins
|
|||
)
|
||||
ck := bank.NewCoinKeeper(accountMapper)
|
||||
params := paramsNoInflation()
|
||||
mapper.saveParams(params)
|
||||
mapper.setParams(params)
|
||||
|
||||
// fill all the addresses with some coins
|
||||
for _, addr := range addrs {
|
||||
|
@ -167,7 +167,7 @@ func candidatesFromAddrs(mapper Mapper, addrs []crypto.Address, amts []int64) {
|
|||
Liabilities: sdk.NewRat(amts[i]),
|
||||
VotingPower: sdk.NewRat(amts[i]),
|
||||
}
|
||||
mapper.saveCandidate(c)
|
||||
mapper.setCandidate(c)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
func Tick(ctx sdk.Context, m Mapper) (change []*abci.Validator, err error) {
|
||||
|
||||
// retrieve params
|
||||
params := m.loadParams()
|
||||
gs := m.loadGlobalState()
|
||||
params := m.getParams()
|
||||
gs := m.getGlobalState()
|
||||
height := ctx.BlockHeight()
|
||||
|
||||
// Process Validator Provisions
|
||||
|
@ -46,7 +46,7 @@ func processProvisions(m Mapper, gs *GlobalState, params Params) {
|
|||
// XXX XXX XXX XXX XXX XXX XXX XXX XXX
|
||||
|
||||
// save the params
|
||||
m.saveGlobalState(gs)
|
||||
m.setGlobalState(gs)
|
||||
}
|
||||
|
||||
// get the next inflation rate for the hour
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
|
||||
func TestGetInflation(t *testing.T) {
|
||||
_, _, mapper, _ := createTestInput(t, nil, false, 0)
|
||||
params := mapper.loadParams()
|
||||
gs := mapper.loadGlobalState()
|
||||
params := mapper.getParams()
|
||||
gs := mapper.getGlobalState()
|
||||
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedPool / TotalSupply
|
||||
|
@ -54,8 +54,8 @@ func TestGetInflation(t *testing.T) {
|
|||
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
_, _, mapper, _ := createTestInput(t, nil, false, 0)
|
||||
params := mapper.loadParams()
|
||||
gs := mapper.loadGlobalState()
|
||||
params := mapper.getParams()
|
||||
gs := mapper.getGlobalState()
|
||||
|
||||
// create some candidates some bonded, some unbonded
|
||||
candidates := candidatesFromAddrsEmpty(addrs)
|
||||
|
@ -66,7 +66,7 @@ func TestProcessProvisions(t *testing.T) {
|
|||
mintedTokens := int64((i + 1) * 10000000)
|
||||
gs.TotalSupply += mintedTokens
|
||||
candidate.addTokens(mintedTokens, gs)
|
||||
mapper.saveCandidate(candidate)
|
||||
mapper.setCandidate(candidate)
|
||||
}
|
||||
var totalSupply int64 = 550000000
|
||||
var bondedShares int64 = 150000000
|
||||
|
|
214
x/stake/tx.go
214
x/stake/tx.go
|
@ -1,214 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// name to idetify transaction types
|
||||
var Name = "stake"
|
||||
|
||||
//Verify interface at compile time
|
||||
var _, _, _, _ sdk.Msg = &MsgDeclareCandidacy{}, &MsgEditCandidacy{}, &MsgDelegate{}, &MsgUnbond{}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgAddr - struct for bonding or unbonding transactions
|
||||
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 Name }
|
||||
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
|
||||
func (msg MsgAddr) ValidateBasic() sdk.Error {
|
||||
if msg.Address == nil {
|
||||
return ErrCandidateEmpty()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDeclareCandidacy - struct for unbonding transactions
|
||||
type MsgDeclareCandidacy struct {
|
||||
MsgAddr
|
||||
Description
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
}
|
||||
|
||||
func NewMsgDeclareCandidacy(address sdk.Address, pubkey crypto.PubKey, bond sdk.Coin, 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 := msg.MsgAddr.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = validateCoin(msg.Bond)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(CodeInvalidInput, "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 := msg.MsgAddr.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(CodeInvalidInput, "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 := msg.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(address sdk.Address, shares string) MsgUnbond {
|
||||
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 := msg.MsgAddr.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Shares == "MAX" {
|
||||
return ErrCandidateEmpty()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
// helper
|
||||
|
||||
func validateCoin(coin sdk.Coin) sdk.Error {
|
||||
coins := sdk.Coins{coin}
|
||||
if !coins.IsValid() {
|
||||
return sdk.ErrInvalidCoins(coins)
|
||||
}
|
||||
if !coins.IsPositive() {
|
||||
return sdk.ErrInvalidCoins(coins) // XXX: add "Amount must be > 0" ?
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -12,14 +12,8 @@ type Params struct {
|
|||
InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate
|
||||
GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms
|
||||
|
||||
MaxVals uint16 `json:"max_vals"` // maximum number of validators
|
||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
|
||||
// gas costs for txs
|
||||
GasDeclareCandidacy int64 `json:"gas_declare_candidacy"`
|
||||
GasEditCandidacy int64 `json:"gas_edit_candidacy"`
|
||||
GasDelegate int64 `json:"gas_delegate"`
|
||||
GasUnbond int64 `json:"gas_unbond"`
|
||||
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
|
||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
}
|
||||
|
||||
func defaultParams() Params {
|
||||
|
@ -28,12 +22,8 @@ func defaultParams() Params {
|
|||
InflationMax: sdk.NewRat(20, 100),
|
||||
InflationMin: sdk.NewRat(7, 100),
|
||||
GoalBonded: sdk.NewRat(67, 100),
|
||||
MaxVals: 100,
|
||||
MaxValidators: 100,
|
||||
BondDenom: "fermion",
|
||||
GasDeclareCandidacy: 20,
|
||||
GasEditCandidacy: 20,
|
||||
GasDelegate: 20,
|
||||
GasUnbond: 20,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,12 +132,10 @@ const (
|
|||
// bond shares is based on the amount of coins delegated divided by the current
|
||||
// exchange rate. Voting power can be calculated as total bonds multiplied by
|
||||
// exchange rate.
|
||||
|
||||
// XXX update to use Address as the main key NOT the pubkey
|
||||
type Candidate struct {
|
||||
Status CandidateStatus `json:"status"` // Bonded status
|
||||
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
|
||||
Address sdk.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here
|
||||
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
|
||||
Assets sdk.Rat `json:"assets"` // total shares of a global hold pools TODO custom type PoolShares
|
||||
Liabilities sdk.Rat `json:"liabilities"` // total shares issued to a candidate's delegators TODO custom type DelegatorShares
|
||||
VotingPower sdk.Rat `json:"voting_power"` // Voting power if considered a validator
|
||||
|
@ -163,11 +151,11 @@ type Description struct {
|
|||
}
|
||||
|
||||
// NewCandidate - initialize a new candidate
|
||||
func NewCandidate(pubKey crypto.PubKey, address sdk.Address, description Description) *Candidate {
|
||||
func NewCandidate(address sdk.Address, pubKey crypto.PubKey, description Description) *Candidate {
|
||||
return &Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pubKey,
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Assets: sdk.ZeroRat,
|
||||
Liabilities: sdk.ZeroRat,
|
||||
VotingPower: sdk.ZeroRat,
|
||||
|
@ -226,6 +214,11 @@ func (c *Candidate) validator() Validator {
|
|||
}
|
||||
}
|
||||
|
||||
//XXX updateDescription function
|
||||
//XXX enforce limit to number of description characters
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// Validator is one of the top Candidates
|
||||
type Validator struct {
|
||||
Address sdk.Address `json:"address"` // Address of validator
|
||||
|
@ -256,7 +249,9 @@ type Candidates []*Candidate
|
|||
// DelegatorBond represents the bond with tokens held by an account. It is
|
||||
// owned by one delegator, and is associated with the voting power of one
|
||||
// pubKey.
|
||||
// TODO better way of managing space
|
||||
type DelegatorBond struct {
|
||||
Address sdk.Address `json:"pub_key"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Address sdk.Address `json:"address"`
|
||||
CandidateAddr sdk.Address `json:"candidate_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue