Merge PR #1119: Unbonding, Redelegation
* stake/fees spec updates * staking overview.md revisions, moving files * docs reorganization * staking spec state revisions * transaction stake updates * complete staking spec update * WIP adding unbonding/redelegation commands * added msg types for unbonding, redelegation * stake sub-package reorg * working stake reorg * modify lcd tests to not use hardcoded json strings * add description update * index keys * key managment for unbonding redelegation complete * update stake errors * completed handleMsgCompleteUnbonding fn * updated to use begin/complete unbonding/redelegation * fix token shares bug * develop docs into unbonding * got non-tests compiling after merge develop * working fixing tests * PrivlegedKeeper -> PrivilegedKeeper * tests compile * fix some tests * fixing tests * remove PrivilegedKeeper * get unbonding bug * only rpc sig verification failed tests now * move percent unbonding/redelegation to the CLI and out of handler logic * remove min unbonding height * add lcd txs * add pool sanity checks, fix a buncha tests * fix ante. set lcd log to debug (#1322) * redelegation tests, adding query functionality for bonds * add self-delegations at genesis ref #1165 * PR comments (mostly) addressed * cleanup, added Query LCD functionality * test cleanup/fixes * fix governance test * SlashValidatorSet -> ValidatorSet * changelog * stake lcd fix * x/auth: fix chainID in ante * fix lcd test * fix lint, update lint make command for spelling * lowercase error string * don't expose coinkeeper in staking * remove a few duplicate lines in changelog * chain_id in stake lcd tests * added transient redelegation * 'transient' => 'transitive' * Re-add nolint instruction * Fix tiny linter error
This commit is contained in:
parent
d6df6b07d1
commit
6f140d7296
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -12,6 +12,14 @@ BREAKING CHANGES
|
|||
* Removed MsgChangePubKey from auth
|
||||
* Removed setPubKey from account mapper
|
||||
* Removed GetMemo from Tx (it is still on StdTx)
|
||||
* [cli] rearranged commands under subcommands
|
||||
* [stake] remove Tick and add EndBlocker
|
||||
* [stake] introduce concept of unbonding for delegations and validators
|
||||
* `gaiacli stake unbond` replaced with `gaiacli stake begin-unbonding`
|
||||
* introduced:
|
||||
* `gaiacli stake complete-unbonding`
|
||||
* `gaiacli stake begin-redelegation`
|
||||
* `gaiacli stake complete-redelegation`
|
||||
|
||||
FEATURES
|
||||
* [gaiacli] You can now attach a simple text-only memo to any transaction, with the `--memo` flag
|
||||
|
@ -29,7 +37,11 @@ FEATURES
|
|||
* [types] Switches internal representation of Int/Uint/Rat to use pointers
|
||||
* [gaiad] unsafe_reset_all now resets addrbook.json
|
||||
|
||||
FIXES
|
||||
FIXES
|
||||
* [gaia] Added self delegation for validators in the genesis creation
|
||||
* [lcd] tests now don't depend on raw json text
|
||||
* [stake] error strings lower case
|
||||
* [stake] pool loose tokens now accounts for unbonding and unbonding tokens not associated with any validator
|
||||
* \#1259 - fix bug where certain tests that could have a nil pointer in defer
|
||||
* \#1052 - Make all now works
|
||||
* Retry on HTTP request failure in CLI tests, add option to retry tests in Makefile
|
||||
|
@ -39,6 +51,16 @@ FIXES
|
|||
* \#1353 - CLI: Show pool shares fractions in human-readable format
|
||||
* \#1258 - printing big.rat's can no longer overflow int64
|
||||
|
||||
IMPROVEMENTS
|
||||
* bank module uses go-wire codec instead of 'encoding/json'
|
||||
* auth module uses go-wire codec instead of 'encoding/json'
|
||||
* revised use of endblock and beginblock
|
||||
* [stake] module reorganized to include `types` and `keeper` package
|
||||
* [stake] keeper always loads the store (instead passing around which doesn't really boost efficiency)
|
||||
* [stake] edit-validator changes now can use the keyword [do-not-modify] to not modify unspecified `--flag` (aka won't set them to `""` value)
|
||||
* [types] added common tag constants
|
||||
* [stake] offload more generic functionality from the handler into the keeper
|
||||
|
||||
## 0.19.0
|
||||
|
||||
*June 13, 2018*
|
||||
|
|
2
Makefile
2
Makefile
|
@ -108,7 +108,7 @@ test_cover:
|
|||
@bash tests/test_cover.sh
|
||||
|
||||
test_lint:
|
||||
gometalinter.v2 --disable-all --enable='golint' --vendor ./...
|
||||
gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --vendor ./...
|
||||
|
||||
benchmark:
|
||||
@go test -bench=. $(PACKAGES_NOCLITEST)
|
||||
|
|
|
@ -598,6 +598,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
|||
// Write the Deliver state and commit the MultiStore
|
||||
app.deliverState.ms.Write()
|
||||
commitID := app.cms.Commit()
|
||||
// TODO: this is missing a module identifier and dumps byte array
|
||||
app.Logger.Debug("Commit synced",
|
||||
"commit", commitID,
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
func TestKeys(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// get seed
|
||||
|
@ -218,7 +218,7 @@ func TestValidators(t *testing.T) {
|
|||
func TestCoinSend(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
||||
|
@ -260,7 +260,7 @@ func TestCoinSend(t *testing.T) {
|
|||
func TestIBCTransfer(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
acc := getAccount(t, port, addr)
|
||||
|
@ -289,7 +289,7 @@ func TestIBCTransfer(t *testing.T) {
|
|||
func TestTxs(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// query wrong
|
||||
|
@ -378,13 +378,13 @@ func TestValidatorsQuery(t *testing.T) {
|
|||
func TestBonding(t *testing.T) {
|
||||
name, password, denom := "test", "1234567890", "steak"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
validator1Owner := pks[0].Address()
|
||||
|
||||
// create bond TX
|
||||
resultTx := doBond(t, port, seed, name, password, addr, validator1Owner)
|
||||
resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was committed
|
||||
|
@ -405,7 +405,7 @@ func TestBonding(t *testing.T) {
|
|||
// testing unbonding
|
||||
|
||||
// create unbond TX
|
||||
resultTx = doUnbond(t, port, seed, name, password, addr, validator1Owner)
|
||||
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// query validator
|
||||
|
@ -416,12 +416,13 @@ func TestBonding(t *testing.T) {
|
|||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// TODO fix shares fn in staking
|
||||
// should the sender should have not received any coins as the unbonding has only just begun
|
||||
// query sender
|
||||
//acc := getAccount(t, sendAddr)
|
||||
//coins := acc.GetCoins()
|
||||
//assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
|
||||
acc = getAccount(t, port, addr)
|
||||
coins = acc.GetCoins()
|
||||
assert.Equal(t, int64(40), coins.AmountOf("steak").Int64())
|
||||
|
||||
// TODO add redelegation, need more complex capabilities such to mock context and
|
||||
}
|
||||
|
||||
func TestSubmitProposal(t *testing.T) {
|
||||
|
@ -572,6 +573,8 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Add
|
|||
receiveAddr := receiveInfo.PubKey.Address()
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, port, addr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
|
@ -584,13 +587,14 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Add
|
|||
"account_number":%d,
|
||||
"sequence": %d,
|
||||
"gas": 100000,
|
||||
"chain_id": "%s",
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}`, name, password, accnum, sequence, "steak"))
|
||||
}`, name, password, accnum, sequence, chainID, "steak"))
|
||||
res, body := Request(t, port, "POST", "/ibc/testchain/"+receiveAddrBech+"/send", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
|
@ -606,7 +610,7 @@ func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.A
|
|||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// get the account to get the sequence
|
||||
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/bonding_status/"+validatorAddrBech, nil)
|
||||
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/delegation/"+validatorAddrBech, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var bond stake.Delegation
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bond)
|
||||
|
@ -614,7 +618,7 @@ func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.A
|
|||
return bond
|
||||
}
|
||||
|
||||
func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
|
@ -623,6 +627,8 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
|
|||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
|
@ -630,15 +636,19 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
|
|||
"account_number": %d,
|
||||
"sequence": %d,
|
||||
"gas": 10000,
|
||||
"delegate": [
|
||||
"chain_id": "%s",
|
||||
"delegations": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"bond": { "denom": "%s", "amount": 60 }
|
||||
}
|
||||
],
|
||||
"unbond": []
|
||||
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech, "steak"))
|
||||
"begin_unbondings": [],
|
||||
"complete_unbondings": [],
|
||||
"begin_redelegates": [],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorAddrBech, "steak"))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
|
@ -649,7 +659,9 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
|
|||
return results[0]
|
||||
}
|
||||
|
||||
func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doBeginUnbonding(t *testing.T, port, seed, name, password string,
|
||||
delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
|
@ -658,6 +670,8 @@ func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, va
|
|||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
|
@ -665,15 +679,64 @@ func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, va
|
|||
"account_number": %d,
|
||||
"sequence": %d,
|
||||
"gas": 10000,
|
||||
"delegate": [],
|
||||
"unbond": [
|
||||
"chain_id": "%s",
|
||||
"delegations": [],
|
||||
"begin_unbondings": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"shares": "30"
|
||||
}
|
||||
]
|
||||
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech))
|
||||
],
|
||||
"complete_unbondings": [],
|
||||
"begin_redelegates": [],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorAddrBech))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results []ctypes.ResultBroadcastTxCommit
|
||||
err := cdc.UnmarshalJSON([]byte(body), &results)
|
||||
require.Nil(t, err)
|
||||
|
||||
return results[0]
|
||||
}
|
||||
|
||||
func doBeginRedelegation(t *testing.T, port, seed, name, password string,
|
||||
delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorSrcAddrBech := sdk.MustBech32ifyVal(validatorSrcAddr)
|
||||
validatorDstAddrBech := sdk.MustBech32ifyVal(validatorDstAddr)
|
||||
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": %d,
|
||||
"sequence": %d,
|
||||
"gas": 10000,
|
||||
"chain_id": "%s",
|
||||
"delegations": [],
|
||||
"begin_unbondings": [],
|
||||
"complete_unbondings": [],
|
||||
"begin_redelegates": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
"validator_src_addr": "%s",
|
||||
"validator_dst_addr": "%s",
|
||||
"shares": "30"
|
||||
}
|
||||
],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorSrcAddrBech, validatorDstAddrBech))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
|
|
|
@ -100,13 +100,13 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
|
|||
config.TxIndex.IndexAllTags = true
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
logger = log.NewFilter(logger, log.AllowDebug())
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
privVal.Reset()
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
cdc = gapp.MakeCodec()
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
|
@ -146,6 +146,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
|
|||
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
genesisState.StakeData.Pool.LooseTokens += 100
|
||||
}
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
|
|
|
@ -3,6 +3,7 @@ package app
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
@ -17,8 +18,8 @@ import (
|
|||
|
||||
var (
|
||||
// bonded tokens given to genesis validators/accounts
|
||||
freeFermionVal = sdk.NewInt(100)
|
||||
freeFermionsAcc = sdk.NewInt(50)
|
||||
freeFermionVal = int64(100)
|
||||
freeFermionsAcc = int64(50)
|
||||
)
|
||||
|
||||
// State to Unmarshal
|
||||
|
@ -124,7 +125,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.Address, name st
|
|||
|
||||
validator = tmtypes.GenesisValidator{
|
||||
PubKey: pk,
|
||||
Power: freeFermionVal.Int64(),
|
||||
Power: freeFermionVal,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -155,22 +156,33 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
|
|||
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
|
||||
accAuth.Coins = sdk.Coins{
|
||||
{genTx.Name + "Token", sdk.NewInt(1000)},
|
||||
{"steak", freeFermionsAcc},
|
||||
{"steak", sdk.NewInt(freeFermionsAcc)},
|
||||
}
|
||||
acc := NewGenesisAccount(&accAuth)
|
||||
genaccs[i] = acc
|
||||
stakeData.Pool.LooseUnbondedTokens = stakeData.Pool.LooseUnbondedTokens.Add(freeFermionsAcc) // increase the supply
|
||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionsAcc // increase the supply
|
||||
|
||||
// add the validator
|
||||
if len(genTx.Name) > 0 {
|
||||
desc := stake.NewDescription(genTx.Name, "", "", "")
|
||||
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
|
||||
validator.PoolShares = stake.NewBondedShares(sdk.NewRatFromInt(freeFermionVal))
|
||||
|
||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionVal // increase the supply
|
||||
|
||||
// add some new shares to the validator
|
||||
var issuedDelShares sdk.Rat
|
||||
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, freeFermionVal)
|
||||
stakeData.Validators = append(stakeData.Validators, validator)
|
||||
|
||||
// pool logic
|
||||
stakeData.Pool.BondedTokens = stakeData.Pool.BondedTokens.Add(freeFermionVal)
|
||||
stakeData.Pool.BondedShares = sdk.NewRatFromInt(stakeData.Pool.BondedTokens)
|
||||
// create the self-delegation from the issuedDelShares
|
||||
delegation := stake.Delegation{
|
||||
DelegatorAddr: validator.Owner,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: issuedDelShares,
|
||||
Height: 0,
|
||||
}
|
||||
|
||||
stakeData.Bonds = append(stakeData.Bonds, delegation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ func main() {
|
|||
stakecmd.GetCmdCreateValidator(cdc),
|
||||
stakecmd.GetCmdEditValidator(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
stakecmd.GetCmdUnbond("stake", cdc),
|
||||
stakecmd.GetCmdRedelegate("stake", cdc),
|
||||
slashingcmd.GetCmdUnrevoke(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
|
|
|
@ -37,8 +37,8 @@ processProvisions():
|
|||
|
||||
provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr)
|
||||
|
||||
pool.LooseUnbondedTokens += provisions
|
||||
feePool += LooseUnbondedTokens
|
||||
pool.LooseTokens += provisions
|
||||
feePool += LooseTokens
|
||||
|
||||
setPool(pool)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ information, etc.
|
|||
|
||||
```golang
|
||||
type Pool struct {
|
||||
LooseUnbondedTokens int64 // tokens not associated with any validator
|
||||
LooseTokens int64 // tokens not associated with any validator
|
||||
UnbondedTokens int64 // reserve of unbonded tokens held with validators
|
||||
UnbondingTokens int64 // tokens moving from bonded to unbonded pool
|
||||
BondedTokens int64 // reserve of bonded tokens
|
||||
|
|
|
@ -172,7 +172,8 @@ func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage,
|
|||
app.accountMapper.IterateAccounts(ctx, appendAccount)
|
||||
|
||||
genState := types.GenesisState{
|
||||
Accounts: accounts,
|
||||
Accounts: accounts,
|
||||
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
|
||||
}
|
||||
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
|
||||
if err != nil {
|
||||
|
|
|
@ -51,6 +51,10 @@ func main() {
|
|||
// add query/post commands (custom to binary)
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
stakecmd.GetCmdQueryValidator("stake", cdc),
|
||||
stakecmd.GetCmdQueryValidators("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegation("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegations("stake", cdc),
|
||||
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
|
||||
|
@ -62,7 +66,7 @@ func main() {
|
|||
stakecmd.GetCmdCreateValidator(cdc),
|
||||
stakecmd.GetCmdEditValidator(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
stakecmd.GetCmdUnbond("stake", cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
|
|
|
@ -110,7 +110,9 @@ func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - r
|
|||
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
|
||||
func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 }
|
||||
func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than
|
||||
func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal
|
||||
func (r Rat) LT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == -1 } // less than
|
||||
func (r Rat) LTE(r2 Rat) bool { return !r.GT(r2) } // less than or equal
|
||||
func (r Rat) Mul(r2 Rat) Rat { return Rat{new(big.Rat).Mul(r.Rat, r2.Rat)} } // Mul - multiplication
|
||||
func (r Rat) Quo(r2 Rat) Rat { return Rat{new(big.Rat).Quo(r.Rat, r2.Rat)} } // Quo - quotient
|
||||
func (r Rat) Add(r2 Rat) Rat { return Rat{new(big.Rat).Add(r.Rat, r2.Rat)} } // Add - addition
|
||||
|
|
|
@ -229,7 +229,7 @@ func TestSerializationText(t *testing.T) {
|
|||
bz, err := r.MarshalText()
|
||||
require.NoError(t, err)
|
||||
|
||||
var r2 Rat = Rat{new(big.Rat)}
|
||||
var r2 = Rat{new(big.Rat)}
|
||||
err = r2.UnmarshalText(bz)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2)
|
||||
|
|
|
@ -59,8 +59,9 @@ type ValidatorSet interface {
|
|||
IterateValidatorsBonded(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
Validator(Context, Address) Validator // get a particular validator by owner address
|
||||
TotalPower(Context) Rat // total power of the validator set
|
||||
Validator(Context, Address) Validator // get a particular validator by owner address
|
||||
TotalPower(Context) Rat // total power of the validator set
|
||||
|
||||
Slash(Context, crypto.PubKey, int64, Rat) // slash the validator and delegators of the validator, specifying offence height & slash fraction
|
||||
Revoke(Context, crypto.PubKey) // revoke a validator
|
||||
Unrevoke(Context, crypto.PubKey) // unrevoke a validator
|
||||
|
|
|
@ -21,8 +21,8 @@ func (t Tags) AppendTag(k string, v []byte) Tags {
|
|||
}
|
||||
|
||||
// Append two lists of tags
|
||||
func (t Tags) AppendTags(a Tags) Tags {
|
||||
return append(t, a...)
|
||||
func (t Tags) AppendTags(tags Tags) Tags {
|
||||
return append(t, tags...)
|
||||
}
|
||||
|
||||
// Turn tags into KVPair list
|
||||
|
@ -51,3 +51,13 @@ func NewTags(tags ...interface{}) Tags {
|
|||
func MakeTag(k string, v []byte) Tag {
|
||||
return Tag{Key: []byte(k), Value: v}
|
||||
}
|
||||
|
||||
//__________________________________________________
|
||||
|
||||
// common tags
|
||||
var (
|
||||
TagAction = "action"
|
||||
TagSrcValidator = "source-validator"
|
||||
TagDstValidator = "destination-validator"
|
||||
TagDelegator = "delegator"
|
||||
)
|
||||
|
|
12
wire/wire.go
12
wire/wire.go
|
@ -35,3 +35,15 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
|||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// generic sealed codec to be used throughout sdk
|
||||
var Cdc *Codec
|
||||
|
||||
func init() {
|
||||
cdc := NewCodec()
|
||||
RegisterCrypto(cdc)
|
||||
Cdc = cdc
|
||||
//Cdc = cdc.Seal() // TODO uncomment once amino upgraded to 0.9.10
|
||||
}
|
||||
|
|
|
@ -57,7 +57,11 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
|
||||
|
||||
stakeGenesis := stake.DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
|
||||
stake.InitGenesis(ctx, stakeKeeper, stakeGenesis)
|
||||
InitGenesis(ctx, keeper, DefaultGenesisState())
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,9 @@ func getEndBlocker(keeper stake.Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
stake.InitGenesis(ctx, keeper, stake.DefaultGenesisState())
|
||||
stakeGenesis := stake.DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
stake.InitGenesis(ctx, keeper, stakeGenesis)
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,41 +12,16 @@ const (
|
|||
// Default slashing codespace
|
||||
DefaultCodespace sdk.CodespaceType = 10
|
||||
|
||||
// Invalid validator
|
||||
CodeInvalidValidator CodeType = 201
|
||||
// Validator jailed
|
||||
CodeValidatorJailed CodeType = 202
|
||||
CodeInvalidValidator CodeType = 101
|
||||
CodeValidatorJailed CodeType = 102
|
||||
)
|
||||
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
|
||||
}
|
||||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
|
||||
}
|
||||
|
||||
func codeToDefaultMsg(code CodeType) string {
|
||||
switch code {
|
||||
case CodeInvalidValidator:
|
||||
return "invalid Validator"
|
||||
case CodeValidatorJailed:
|
||||
return "validator Jailed"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||
pool := sk.GetPool(ctx)
|
||||
require.Equal(t, int64(100), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(100), pool.BondedTokens)
|
||||
|
||||
// 51st block missed
|
||||
ctx = ctx.WithBlockHeight(height)
|
||||
|
@ -109,7 +109,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
|
||||
// validator should have been slashed
|
||||
pool = sk.GetPool(ctx)
|
||||
require.Equal(t, int64(99), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(99), pool.BondedTokens)
|
||||
|
||||
// validator start height should have been changed
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
|
||||
|
@ -167,5 +167,5 @@ func TestHandleNewValidator(t *testing.T) {
|
|||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||
pool := sk.GetPool(ctx)
|
||||
require.Equal(t, int64(100), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(100), pool.BondedTokens)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// TODO remove dependencies on staking (should only refer to validator set type from sdk)
|
||||
|
||||
var (
|
||||
addrs = []sdk.Address{
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6160"),
|
||||
|
@ -61,7 +63,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep
|
|||
ck := bank.NewKeeper(accountMapper)
|
||||
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
|
||||
genesis := stake.DefaultGenesisState()
|
||||
genesis.Pool.LooseUnbondedTokens = initCoins.MulRaw(int64(len(addrs)))
|
||||
genesis.Pool.LooseTokens = initCoins.MulRaw(int64(len(addrs))).Int64()
|
||||
stake.InitGenesis(ctx, sk, genesis)
|
||||
for _, addr := range addrs {
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
|
@ -89,9 +91,9 @@ func testAddr(addr string) sdk.Address {
|
|||
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
|
||||
return stake.MsgCreateValidator{
|
||||
Description: stake.Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Description: stake.Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
SelfDelegation: sdk.Coin{"steak", amt},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ var (
|
|||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{sdk.NewCoin("foocoin", 10)}
|
||||
coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{sdk.NewCoin("foocoin", 0)},
|
||||
sdk.Coins{{"foocoin", sdk.NewInt(0)}},
|
||||
100000,
|
||||
}
|
||||
)
|
||||
|
@ -60,7 +60,9 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
InitGenesis(ctx, keeper, DefaultGenesisState())
|
||||
stakeGenesis := DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
InitGenesis(ctx, keeper, stakeGenesis)
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
@ -93,8 +95,8 @@ func checkDelegation(t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
|
|||
func TestStakeMsgs(t *testing.T) {
|
||||
mapp, keeper := getMockApp(t)
|
||||
|
||||
genCoin := sdk.NewCoin("steak", 42)
|
||||
bondCoin := sdk.NewCoin("steak", 10)
|
||||
genCoin := sdk.Coin{"steak", sdk.NewInt(42)}
|
||||
bondCoin := sdk.Coin{"steak", sdk.NewInt(10)}
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
|
@ -148,10 +150,14 @@ func TestStakeMsgs(t *testing.T) {
|
|||
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Unbond
|
||||
// Begin Unbonding
|
||||
|
||||
unbondMsg := NewMsgUnbond(addr2, addr1, "MAX")
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unbondMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10))
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
|
||||
// delegation should exist anymore
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
|
||||
|
||||
// balance should be the same because bonding not yet complete
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
}
|
||||
|
|
|
@ -6,11 +6,14 @@ import (
|
|||
|
||||
// nolint
|
||||
const (
|
||||
FlagAddressDelegator = "address-delegator"
|
||||
FlagAddressValidator = "address-validator"
|
||||
FlagPubKey = "pubkey"
|
||||
FlagAmount = "amount"
|
||||
FlagShares = "shares"
|
||||
FlagAddressDelegator = "address-delegator"
|
||||
FlagAddressValidator = "address-validator"
|
||||
FlagAddressValidatorSrc = "addr-validator-source"
|
||||
FlagAddressValidatorDst = "addr-validator-dest"
|
||||
FlagPubKey = "pubkey"
|
||||
FlagAmount = "amount"
|
||||
FlagSharesAmount = "shares-amount"
|
||||
FlagSharesPercent = "shares-percent"
|
||||
|
||||
FlagMoniker = "moniker"
|
||||
FlagIdentity = "keybase-sig"
|
||||
|
@ -20,22 +23,26 @@ const (
|
|||
|
||||
// common flagsets to add to various functions
|
||||
var (
|
||||
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
)
|
||||
|
||||
func init() {
|
||||
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220")
|
||||
fsAmount.String(FlagAmount, "1steak", "Amount of coins to bond")
|
||||
fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)")
|
||||
fsDescription.String(FlagMoniker, "", "validator name")
|
||||
fsDescription.String(FlagIdentity, "", "optional keybase signature")
|
||||
fsDescription.String(FlagWebsite, "", "optional website")
|
||||
fsDescription.String(FlagDetails, "", "optional details")
|
||||
fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal")
|
||||
fsShares.String(FlagSharesPercent, "", "Percent of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1")
|
||||
fsDescription.String(FlagMoniker, "[do-not-modify]", "validator name")
|
||||
fsDescription.String(FlagIdentity, "[do-not-modify]", "optional keybase signature")
|
||||
fsDescription.String(FlagWebsite, "[do-not-modify]", "optional website")
|
||||
fsDescription.String(FlagDetails, "[do-not-modify]", "optional details")
|
||||
fsValidator.String(FlagAddressValidator, "", "hex address of the validator")
|
||||
fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator")
|
||||
fsRedelegation.String(FlagAddressValidatorSrc, "", "hex address of the source validator")
|
||||
fsRedelegation.String(FlagAddressValidatorDst, "", "hex address of the destination validator")
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
|
@ -105,14 +105,14 @@ func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single delegation bond
|
||||
// get the command to query a single delegation
|
||||
func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegation",
|
||||
Short: "Query a delegations bond based on address and validator address",
|
||||
Short: "Query a delegation based on address and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
addr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -122,26 +122,26 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
key := stake.GetDelegationKey(delAddr, addr, cdc)
|
||||
key := stake.GetDelegationKey(delAddr, valAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the bond
|
||||
bond := new(stake.Delegation)
|
||||
// parse out the delegation
|
||||
delegation := new(stake.Delegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := bond.HumanReadableString()
|
||||
resp, err := delegation.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, bond)
|
||||
output, err := wire.MarshalJSONIndent(cdc, bond)
|
||||
cdc.MustUnmarshalBinary(res, delegation)
|
||||
output, err := wire.MarshalJSONIndent(cdc, delegation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the validators bonded to a delegation
|
||||
// get the command to query all the delegations made from one delegator
|
||||
func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegations [delegator-addr]",
|
||||
|
@ -196,3 +196,190 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single unbonding-delegation record
|
||||
func GetCmdQueryUnbondingDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegation",
|
||||
Short: "Query an unbonding-delegation record based on delegator and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := stake.GetUBDKey(delAddr, valAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the unbonding delegation
|
||||
ubd := new(stake.UnbondingDelegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := ubd.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, ubd)
|
||||
output, err := wire.MarshalJSONIndent(cdc, ubd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the unbonding-delegation records for a delegator
|
||||
func GetCmdQueryUnbondingDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegations [delegator-addr]",
|
||||
Short: "Query all unbonding-delegations records for one delegator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := stake.GetUBDsKey(delegatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the validators
|
||||
var ubds []stake.UnbondingDelegation
|
||||
for _, KV := range resKVs {
|
||||
var ubd stake.UnbondingDelegation
|
||||
cdc.MustUnmarshalBinary(KV.Value, &ubd)
|
||||
ubds = append(ubds, ubd)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, ubds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
|
||||
// TODO output with proofs / machine parseable etc.
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single unbonding-delegation record
|
||||
func GetCmdQueryRedelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegation",
|
||||
Short: "Query an unbonding-delegation record based on delegator and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
valSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := stake.GetREDKey(delAddr, valSrcAddr, valDstAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the unbonding delegation
|
||||
red := new(stake.Redelegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := red.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, red)
|
||||
output, err := wire.MarshalJSONIndent(cdc, red)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the unbonding-delegation records for a delegator
|
||||
func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegations [delegator-addr]",
|
||||
Short: "Query all unbonding-delegations records for one delegator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := stake.GetREDsKey(delegatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the validators
|
||||
var reds []stake.Redelegation
|
||||
for _, KV := range resKVs {
|
||||
var red stake.Redelegation
|
||||
cdc.MustUnmarshalBinary(KV.Value, &red)
|
||||
reds = append(reds, red)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, reds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
|
||||
// TODO output with proofs / machine parseable etc.
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
|
@ -104,11 +105,11 @@ func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
// delegate command
|
||||
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegate",
|
||||
Short: "delegate coins to an existing validator",
|
||||
Short: "delegate liquid tokens to an validator",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
amount, err := sdk.ParseCoin(viper.GetString(FlagAmount))
|
||||
if err != nil {
|
||||
|
@ -143,33 +144,185 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
||||
func GetCmdRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbond",
|
||||
Short: "unbond shares from a validator",
|
||||
Use: "redelegate",
|
||||
Short: "redelegate illiquid tokens from one validator to another",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
GetCmdBeginRedelegate(storeName, cdc),
|
||||
GetCmdCompleteRedelegate(cdc),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// redelegate command
|
||||
func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "begin",
|
||||
Short: "begin redelegation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// check the shares before broadcasting
|
||||
sharesStr := viper.GetString(FlagShares)
|
||||
var shares sdk.Rat
|
||||
if sharesStr != "MAX" {
|
||||
var err error
|
||||
shares, err = sdk.NewRatFromDecimal(sharesStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !shares.GT(sdk.ZeroRat()) {
|
||||
return fmt.Errorf("shares must be positive integer or decimal (ex. 123, 1.23456789)")
|
||||
}
|
||||
var err error
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the shares amount
|
||||
sharesAmountStr := viper.GetString(FlagSharesAmount)
|
||||
sharesPercentStr := viper.GetString(FlagSharesPercent)
|
||||
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
|
||||
delegatorAddr, validatorSrcAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr, sharesAmount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsShares)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getShares(storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercentStr string,
|
||||
delegatorAddr, validatorAddr sdk.Address) (sharesAmount sdk.Rat, err error) {
|
||||
|
||||
switch {
|
||||
case sharesAmountStr != "" && sharesPercentStr != "":
|
||||
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
|
||||
case sharesAmountStr == "" && sharesPercentStr == "":
|
||||
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
|
||||
case sharesAmountStr != "":
|
||||
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
if !sharesAmount.GT(sdk.ZeroRat()) {
|
||||
return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
|
||||
}
|
||||
case sharesPercentStr != "":
|
||||
var sharesPercent sdk.Rat
|
||||
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) {
|
||||
return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
|
||||
}
|
||||
|
||||
// make a query to get the existing delegation shares
|
||||
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resQuery, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
var delegation stake.Delegation
|
||||
err = cdc.UnmarshalBinary(resQuery, &delegation)
|
||||
if err != nil {
|
||||
return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err)
|
||||
}
|
||||
|
||||
sharesAmount = sharesPercent.Mul(delegation.Shares)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// redelegate command
|
||||
func GetCmdCompleteRedelegate(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "complete",
|
||||
Short: "complete redelegation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgCompleteRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdUnbond(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbond",
|
||||
Short: "begin or complete unbonding shares from a validator",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
GetCmdBeginUnbonding(storeName, cdc),
|
||||
GetCmdCompleteUnbonding(cdc),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdBeginUnbonding(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "begin",
|
||||
Short: "begin unbonding",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgUnbond(delegatorAddr, validatorAddr, sharesStr)
|
||||
// get the shares amount
|
||||
sharesAmountStr := viper.GetString(FlagSharesAmount)
|
||||
sharesPercentStr := viper.GetString(FlagSharesPercent)
|
||||
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
|
||||
delegatorAddr, validatorAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sharesAmount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -189,3 +342,35 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
|||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdCompleteUnbonding(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "complete",
|
||||
Short: "complete unbonding",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -13,18 +13,30 @@ import (
|
|||
)
|
||||
|
||||
func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/bonding_status/{validator}",
|
||||
bondingStatusHandlerFn(ctx, "stake", cdc),
|
||||
"/stake/{delegator}/delegation/{validator}",
|
||||
delegationHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/ubd/{validator}",
|
||||
ubdHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/red/{validator_src}/{validator_dst}",
|
||||
redHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/validators",
|
||||
validatorsHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
}
|
||||
|
||||
// http request handler to query delegator bonding status
|
||||
func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
// http request handler to query a delegation
|
||||
func delegationHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
|
@ -51,25 +63,147 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
|||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this bond
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var bond stake.Delegation
|
||||
err = cdc.UnmarshalBinary(res, &bond)
|
||||
var delegation stake.Delegation
|
||||
err = cdc.UnmarshalBinary(res, &delegation)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode bond. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(bond)
|
||||
output, err := cdc.MarshalJSON(delegation)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
// http request handler to query an unbonding-delegation
|
||||
func ubdHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
bech32delegator := vars["delegator"]
|
||||
bech32validator := vars["validator"]
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
key := stake.GetUBDKey(delegatorAddr, validatorAddr, cdc)
|
||||
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var ubd stake.UnbondingDelegation
|
||||
err = cdc.UnmarshalBinary(res, &ubd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode unbonding-delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(ubd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
// http request handler to query an redelegation
|
||||
func redHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
bech32delegator := vars["delegator"]
|
||||
bech32validatorSrc := vars["validator_src"]
|
||||
bech32validatorDst := vars["validator_dst"]
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(bech32validatorSrc)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(bech32validatorDst)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
key := stake.GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr, cdc)
|
||||
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query redelegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var red stake.Redelegation
|
||||
err = cdc.UnmarshalBinary(res, &red)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode redelegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(red)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -24,31 +24,50 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k
|
|||
).Methods("POST")
|
||||
}
|
||||
|
||||
type msgDelegateInput struct {
|
||||
type msgDelegationsInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
type msgUnbondInput struct {
|
||||
type msgBeginRedelegateInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
|
||||
SharesAmount string `json:"shares"`
|
||||
}
|
||||
type msgCompleteRedelegateInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
|
||||
}
|
||||
type msgBeginUnbondingInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
SharesAmount string `json:"shares"`
|
||||
}
|
||||
type msgCompleteUnbondingInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
type editDelegationsBody struct {
|
||||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegate []msgDelegateInput `json:"delegate"`
|
||||
Unbond []msgUnbondInput `json:"unbond"`
|
||||
// request body for edit delegations
|
||||
type EditDelegationsBody struct {
|
||||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegations []msgDelegationsInput `json:"delegations"`
|
||||
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
|
||||
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
|
||||
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
|
||||
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
|
||||
}
|
||||
|
||||
func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var m editDelegationsBody
|
||||
var m EditDelegationsBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
@ -70,24 +89,29 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
|||
}
|
||||
|
||||
// build messages
|
||||
messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond))
|
||||
messages := make([]sdk.Msg, len(m.Delegations)+
|
||||
len(m.BeginRedelegates)+
|
||||
len(m.CompleteRedelegates)+
|
||||
len(m.BeginUnbondings)+
|
||||
len(m.CompleteUnbondings))
|
||||
|
||||
i := 0
|
||||
for _, msg := range m.Delegate {
|
||||
for _, msg := range m.Delegations {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("must use own delegator address"))
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgDelegate{
|
||||
|
@ -97,28 +121,131 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
|||
}
|
||||
i++
|
||||
}
|
||||
for _, msg := range m.Unbond {
|
||||
|
||||
for _, msg := range m.BeginRedelegates {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgBeginRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
SharesAmount: shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.CompleteRedelegates {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgCompleteRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.BeginUnbondings {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgBeginUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
SharesAmount: shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.CompleteUnbondings {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("must use own delegator address"))
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgUnbond{
|
||||
messages[i] = stake.MsgCompleteUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: msg.Shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Delegation 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.
|
||||
type Delegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
func (b Delegation) equal(b2 Delegation) bool {
|
||||
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b.ValidatorAddr, b2.ValidatorAddr) &&
|
||||
b.Height == b2.Height &&
|
||||
b.Shares.Equal(b2.Shares)
|
||||
}
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Delegation = Delegation{}
|
||||
|
||||
// nolint - for sdk.Delegation
|
||||
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
|
||||
func (b Delegation) GetValidator() sdk.Address { return b.ValidatorAddr }
|
||||
func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
|
||||
|
||||
//Human Friendly pretty printer
|
||||
func (b Delegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(b.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechVal, err := sdk.Bech32ifyAcc(b.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp := "Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
resp += fmt.Sprintf("Shares: %s", b.Shares.String())
|
||||
resp += fmt.Sprintf("Height: %d", b.Height)
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
// nolint
|
||||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type CodeType = sdk.CodeType
|
||||
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 4
|
||||
|
||||
// Gaia errors reserve 200 ~ 299.
|
||||
CodeInvalidValidator CodeType = 201
|
||||
CodeInvalidBond CodeType = 202
|
||||
CodeInvalidInput CodeType = 203
|
||||
CodeValidatorJailed CodeType = 204
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
// NOTE: Don't stringer this, we'll put better messages in later.
|
||||
func codeToDefaultMsg(code CodeType) string {
|
||||
switch code {
|
||||
case CodeInvalidValidator:
|
||||
return "invalid Validator"
|
||||
case CodeInvalidBond:
|
||||
return "invalid Bond"
|
||||
case CodeInvalidInput:
|
||||
return "invalid Input"
|
||||
case CodeUnauthorized:
|
||||
return "unauthorized"
|
||||
case CodeInternal:
|
||||
return "internal Error"
|
||||
case CodeUnknownRequest:
|
||||
return "unknown request"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Error constructors
|
||||
|
||||
func ErrNotEnoughBondShares(codespace sdk.CodespaceType, shares string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
|
||||
}
|
||||
func ErrValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "cannot bond to an empty validator")
|
||||
}
|
||||
func ErrBadBondingDenom(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, "invalid coin denomination")
|
||||
}
|
||||
func ErrBadBondingAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, "amount must be > 0")
|
||||
}
|
||||
func ErrNoBondingAcct(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "no bond account for this (address, validator) pair")
|
||||
}
|
||||
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "commission must be positive")
|
||||
}
|
||||
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
|
||||
}
|
||||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "delegator does not exist for that address")
|
||||
}
|
||||
func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator")
|
||||
}
|
||||
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator for this address is currently revoked")
|
||||
}
|
||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "missing signature")
|
||||
}
|
||||
func ErrBondNotNominated(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "cannot bond to non-nominated account")
|
||||
}
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "delegator does not contain validator bond")
|
||||
}
|
||||
func ErrInsufficientFunds(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidInput, "insufficient bond shares")
|
||||
}
|
||||
func ErrBadShares(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidInput, "bad shares provided as input, must be MAX or decimal")
|
||||
}
|
||||
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "error removing validator")
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// TODO group with code from x/bank/errors.go
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
}
|
|
@ -4,63 +4,39 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
Validators []Validator `json:"validators"`
|
||||
Bonds []Delegation `json:"bonds"`
|
||||
}
|
||||
|
||||
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
|
||||
return GenesisState{
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: InitialPool(),
|
||||
Params: DefaultParams(),
|
||||
}
|
||||
}
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
k.setPool(ctx, data.Pool)
|
||||
k.setNewParams(ctx, data.Params)
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
|
||||
keeper.SetPool(ctx, data.Pool)
|
||||
keeper.SetNewParams(ctx, data.Params)
|
||||
keeper.InitIntraTxCounter(ctx)
|
||||
for _, validator := range data.Validators {
|
||||
|
||||
// set validator
|
||||
k.setValidator(ctx, validator)
|
||||
keeper.SetValidator(ctx, validator)
|
||||
|
||||
// manually set indexes for the first time
|
||||
k.setValidatorByPubKeyIndex(ctx, validator)
|
||||
k.setValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
|
||||
keeper.SetValidatorBondedIndex(ctx, validator)
|
||||
}
|
||||
}
|
||||
for _, bond := range data.Bonds {
|
||||
k.setDelegation(ctx, bond)
|
||||
keeper.SetDelegation(ctx, bond)
|
||||
}
|
||||
k.updateBondedValidatorsFull(ctx, store)
|
||||
keeper.UpdateBondedValidatorsFull(ctx)
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
validators := k.getAllValidators(ctx)
|
||||
bonds := k.getAllDelegations(ctx)
|
||||
return GenesisState{
|
||||
func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
validators := keeper.GetAllValidators(ctx)
|
||||
bonds := keeper.GetAllDelegations(ctx)
|
||||
return types.GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
|
@ -69,8 +45,8 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
|||
}
|
||||
|
||||
// WriteValidators - output current validator set
|
||||
func WriteValidators(ctx sdk.Context, k Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
vals = append(vals, tmtypes.GenesisValidator{
|
||||
PubKey: validator.GetPubKey(),
|
||||
Power: validator.GetPower().Evaluate(),
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/tags"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
func NewHandler(k Keeper) sdk.Handler {
|
||||
func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
// NOTE msg already has validate basic run
|
||||
switch msg := msg.(type) {
|
||||
case MsgCreateValidator:
|
||||
case types.MsgCreateValidator:
|
||||
return handleMsgCreateValidator(ctx, msg, k)
|
||||
case MsgEditValidator:
|
||||
case types.MsgEditValidator:
|
||||
return handleMsgEditValidator(ctx, msg, k)
|
||||
case MsgDelegate:
|
||||
case types.MsgDelegate:
|
||||
return handleMsgDelegate(ctx, msg, k)
|
||||
case MsgUnbond:
|
||||
return handleMsgUnbond(ctx, msg, k)
|
||||
case types.MsgBeginRedelegate:
|
||||
return handleMsgBeginRedelegate(ctx, msg, k)
|
||||
case types.MsgCompleteRedelegate:
|
||||
return handleMsgCompleteRedelegate(ctx, msg, k)
|
||||
case types.MsgBeginUnbonding:
|
||||
return handleMsgBeginUnbonding(ctx, msg, k)
|
||||
case types.MsgCompleteUnbonding:
|
||||
return handleMsgCompleteUnbonding(ctx, msg, k)
|
||||
default:
|
||||
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
|
||||
}
|
||||
|
@ -26,25 +34,25 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
}
|
||||
|
||||
// Called every block, process inflation, update validator set
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
|
||||
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// Process Validator Provisions
|
||||
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
|
||||
// Process types.Validator Provisions
|
||||
blockTime := ctx.BlockHeader().Time
|
||||
if pool.InflationLastTime+blockTime >= 3600 {
|
||||
pool.InflationLastTime = blockTime
|
||||
pool = k.processProvisions(ctx)
|
||||
pool = k.ProcessProvisions(ctx)
|
||||
}
|
||||
|
||||
// save the params
|
||||
k.setPool(ctx, pool)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// reset the intra-transaction counter
|
||||
k.setIntraTxCounter(ctx, 0)
|
||||
k.SetIntraTxCounter(ctx, 0)
|
||||
|
||||
// calculate validator set changes
|
||||
ValidatorUpdates = k.getTendermintUpdates(ctx)
|
||||
k.clearTendermintUpdates(ctx)
|
||||
ValidatorUpdates = k.GetTendermintUpdates(ctx)
|
||||
k.ClearTendermintUpdates(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -53,212 +61,150 @@ func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
|
|||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k Keeper) sdk.Result {
|
||||
func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if found {
|
||||
return ErrValidatorExistsAddr(k.codespace).Result()
|
||||
return ErrValidatorAlreadyExists(k.Codespace()).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
if msg.SelfDelegation.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadDenom(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
|
||||
k.setValidator(ctx, validator)
|
||||
k.setValidatorByPubKeyIndex(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("createValidator"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
)
|
||||
k.SetValidator(ctx, validator)
|
||||
k.SetValidatorByPubKeyIndex(ctx, validator)
|
||||
|
||||
// move coins from the msg.Address account to a (self-bond) delegator account
|
||||
// move coins from the msg.Address account to a (self-delegation) delegator account
|
||||
// the validator account and global shares are updated within here
|
||||
delegateTags, err := delegate(ctx, k, msg.ValidatorAddr, msg.Bond, validator)
|
||||
_, err := k.Delegate(ctx, msg.ValidatorAddr, msg.SelfDelegation, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
tags = tags.AppendTags(delegateTags)
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionCreateValidator,
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
tags.Moniker, []byte(msg.Description.Moniker),
|
||||
tags.Identity, []byte(msg.Description.Identity),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgEditValidator(ctx sdk.Context, msg MsgEditValidator, k Keeper) sdk.Result {
|
||||
func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// validator must already be registered
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
return ErrNoValidatorFound(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
// XXX move to types
|
||||
// replace all editable fields (clients should autofill existing values)
|
||||
validator.Description.Moniker = msg.Description.Moniker
|
||||
validator.Description.Identity = msg.Description.Identity
|
||||
validator.Description.Website = msg.Description.Website
|
||||
validator.Description.Details = msg.Description.Details
|
||||
description, err := validator.Description.UpdateDescription(msg.Description)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
validator.Description = description
|
||||
|
||||
k.updateValidator(ctx, validator)
|
||||
k.UpdateValidator(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("editValidator"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
tags.Action, tags.ActionEditValidator,
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
tags.Moniker, []byte(description.Moniker),
|
||||
tags.Identity, []byte(description.Identity),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
||||
func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result {
|
||||
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
return ErrNoValidatorFound(k.Codespace()).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
return ErrBadDenom(k.Codespace()).Result()
|
||||
}
|
||||
if validator.Revoked == true {
|
||||
return ErrValidatorRevoked(k.codespace).Result()
|
||||
return ErrValidatorRevoked(k.Codespace()).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
}
|
||||
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, validator)
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Bond, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionDelegate,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
// common functionality between handlers
|
||||
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
||||
bondAmt sdk.Coin, validator Validator) (sdk.Tags, sdk.Error) {
|
||||
|
||||
// Get or create the delegator bond
|
||||
bond, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
if !found {
|
||||
bond = Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
pool := k.GetPool(ctx)
|
||||
_, _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
|
||||
err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err.Result()
|
||||
}
|
||||
validator, pool, newShares := validator.addTokensFromDel(pool, bondAmt.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
|
||||
k.setPool(ctx, pool)
|
||||
k.setDelegation(ctx, bond)
|
||||
k.updateValidator(ctx, validator)
|
||||
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "validator", validator.Owner.Bytes())
|
||||
return tags, nil
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionBeginUnbonding,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||
func handleMsgCompleteUnbonding(ctx sdk.Context, msg types.MsgCompleteUnbonding, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// check if bond has any shares in it unbond
|
||||
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoDelegatorForAddress(k.codespace).Result()
|
||||
err := k.CompleteUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
var delShares sdk.Rat
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, ActionCompleteUnbonding,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
|
||||
// test that there are enough shares to unbond
|
||||
if msg.Shares == "MAX" {
|
||||
if !bond.Shares.GT(sdk.ZeroRat()) {
|
||||
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
|
||||
}
|
||||
} else {
|
||||
var err sdk.Error
|
||||
delShares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
if bond.Shares.LT(delShares) {
|
||||
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
|
||||
}
|
||||
}
|
||||
|
||||
// get validator
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
|
||||
if msg.Shares == "MAX" {
|
||||
delShares = bond.Shares
|
||||
}
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
bond.Shares = bond.Shares.Sub(delShares)
|
||||
|
||||
// remove the bond
|
||||
revokeValidator := false
|
||||
if bond.Shares.IsZero() {
|
||||
|
||||
// if the bond is the owner of the validator then
|
||||
// trigger a revoke validator
|
||||
if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
|
||||
validator.Revoked == false {
|
||||
revokeValidator = true
|
||||
}
|
||||
|
||||
k.removeDelegation(ctx, bond)
|
||||
} else {
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
k.setDelegation(ctx, bond)
|
||||
}
|
||||
|
||||
// Add the coins
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, returnAmount := validator.removeDelShares(pool, delShares)
|
||||
k.setPool(ctx, pool)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||
|
||||
/////////////////////////////////////
|
||||
// revoke validator if necessary
|
||||
if revokeValidator {
|
||||
validator.Revoked = true
|
||||
}
|
||||
|
||||
validator = k.updateValidator(ctx, validator)
|
||||
|
||||
if validator.DelegatorShares.IsZero() {
|
||||
k.removeValidator(ctx, validator.Owner)
|
||||
}
|
||||
|
||||
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes())
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
|
||||
err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
|
||||
msg.ValidatorDstAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionBeginRedelegation,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgCompleteRedelegate(ctx sdk.Context, msg types.MsgCompleteRedelegate, k keeper.Keeper) sdk.Result {
|
||||
err := k.CompleteRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr, msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionCompleteRedelegation,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -10,38 +9,48 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
keep "github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Description: Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
SelfDelegation: sdk.Coin{"steak", sdk.NewInt(amt)},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt sdk.Int) MsgDelegate {
|
||||
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Bond: sdk.Coin{"steak", sdk.NewInt(amt)},
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve params which are instant
|
||||
func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params {
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 0
|
||||
keeper.SetParams(ctx, params)
|
||||
return params
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
func TestValidatorByPowerIndex(t *testing.T) {
|
||||
validatorAddr, validatorAddr3 := addrs[0], addrs[1]
|
||||
validatorAddr, validatorAddr3 := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
initBond := sdk.NewInt(1000000)
|
||||
initBondStr := "1000"
|
||||
ctx, _, keeper := createTestInput(t, false, initBond)
|
||||
initBond := int64(1000000)
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
|
@ -49,7 +58,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
|||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
require.Equal(t, initBond.Int64(), gotBond,
|
||||
require.Equal(t, initBond, gotBond,
|
||||
"initBond: %v\ngotBond: %v\nbond: %v\n",
|
||||
initBond, gotBond, bond)
|
||||
|
||||
|
@ -57,60 +66,62 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
|||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool := keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
power := keep.GetValidatorsByPowerIndexKey(validator, pool)
|
||||
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||
|
||||
// create a second validator keep it bonded
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, pks[2], sdk.NewInt(1000000))
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000))
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// slash and revoke the first validator
|
||||
keeper.Slash(ctx, pks[0], 0, sdk.NewRat(1, 2))
|
||||
keeper.Revoke(ctx, pks[0])
|
||||
keeper.Slash(ctx, keep.PKs[0], 0, sdk.NewRat(1, 2))
|
||||
keeper.Revoke(ctx, keep.PKs[0])
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded
|
||||
require.Equal(t, int64(500000), validator.PoolShares.Amount.Evaluate()) // ensure is unbonded
|
||||
|
||||
// the old power record should have been deleted as the power changed
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||
|
||||
// but the new power record should have been created
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool = keeper.GetPool(ctx)
|
||||
power2 := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power2))
|
||||
power2 := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
|
||||
|
||||
// inflate a bunch
|
||||
for i := 0; i < 20000; i++ {
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool = keeper.ProcessProvisions(ctx)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
// now the new record power index should be the same as the original record
|
||||
power3 := GetValidatorsByPowerKey(validator, pool)
|
||||
power3 := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.Equal(t, power2, power3)
|
||||
|
||||
// unbond self-delegation
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "MAX")
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\ninitBondStr: %v\n", got, msgUnbond, initBondStr)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(1000000))
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
|
||||
// verify that by power key nolonger exists
|
||||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power3))
|
||||
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power3))
|
||||
}
|
||||
|
||||
func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
|
||||
validatorAddr := addrs[0]
|
||||
pk := pks[0]
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, sdk.NewInt(10))
|
||||
validatorAddr := keep.Addrs[0]
|
||||
pk := keep.PKs[0]
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "%v", got)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
|
@ -124,41 +135,41 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
|||
assert.Equal(t, Description{}, validator.Description)
|
||||
|
||||
// one validator cannot bond twice
|
||||
msgCreateValidator.PubKey = pks[1]
|
||||
msgCreateValidator.PubKey = keep.PKs[1]
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.False(t, got.IsOK(), "%v", got)
|
||||
}
|
||||
|
||||
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
bondAmount := sdk.NewInt(10)
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
bondAmount := int64(10)
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
// first create validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], bondAmount)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.Equal(t, bondAmount, validator.DelegatorShares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().EvaluateInt(), "validator: %v", validator)
|
||||
assert.Equal(t, bondAmount, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().Evaluate(), "validator: %v", validator)
|
||||
|
||||
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, bondAmount, bond.Shares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, bond.Shares.Evaluate())
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
|
||||
assert.Equal(t, bondAmount, pool.BondedShares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, pool.BondedShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, pool.BondedTokens)
|
||||
|
||||
// just send the same msgbond multiple times
|
||||
|
@ -180,14 +191,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
|
||||
|
||||
expBond := sdk.NewInt(int64(i + 1)).Mul(bondAmount)
|
||||
expDelegatorShares := sdk.NewInt(int64(i + 2)).Mul(bondAmount) // (1 self delegation)
|
||||
expDelegatorAcc := initBond.Sub(expBond)
|
||||
expBond := int64(i+1) * bondAmount
|
||||
expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorAcc := sdk.NewInt(initBond - expBond)
|
||||
|
||||
require.Equal(t, bond.Height, int64(i), "Incorrect bond height")
|
||||
|
||||
gotBond := bond.Shares.EvaluateInt()
|
||||
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
|
@ -203,14 +214,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator, delegate
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
|
@ -220,16 +231,19 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.EvaluateInt())
|
||||
assert.Equal(t, initBond.MulRaw(2), validator.PoolShares.Bonded().EvaluateInt())
|
||||
assert.Equal(t, initBond*2, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, initBond*2, validator.PoolShares.Bonded().Evaluate())
|
||||
|
||||
// just send the same msgUnbond multiple times
|
||||
// TODO use decimals here
|
||||
unbondShares, unbondSharesStr := int64(10), "10"
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
unbondShares := sdk.NewRat(10)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
numUnbonds := 5
|
||||
for i := 0; i < numUnbonds; i++ {
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
|
@ -238,12 +252,12 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
|
||||
expBond := initBond.SubRaw(int64(i+1) * unbondShares)
|
||||
expDelegatorShares := initBond.MulRaw(2).SubRaw(int64(i+1) * unbondShares)
|
||||
expDelegatorAcc := initBond.Sub(expBond)
|
||||
expBond := initBond - int64(i+1)*unbondShares.Evaluate()
|
||||
expDelegatorShares := 2*initBond - int64(i+1)*unbondShares.Evaluate()
|
||||
expDelegatorAcc := sdk.NewInt(initBond - expBond)
|
||||
|
||||
gotBond := bond.Shares.EvaluateInt()
|
||||
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
|
@ -263,41 +277,42 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
//1<<63 + 1, // more than int64
|
||||
1<<63 - 1,
|
||||
1 << 31,
|
||||
initBond.Int64(),
|
||||
initBond,
|
||||
}
|
||||
for _, c := range errorCases {
|
||||
unbondShares := strconv.Itoa(int(c))
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares := sdk.NewRat(int64(c))
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.False(t, got.IsOK(), "expected unbond msg to fail")
|
||||
}
|
||||
|
||||
leftBonded := initBond.SubRaw(unbondShares * int64(numUnbonds))
|
||||
leftBonded := initBond - int64(numUnbonds)*unbondShares.Evaluate()
|
||||
|
||||
// should be unable to unbond one more than we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()) + 1)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares = sdk.NewRat(leftBonded + 1)
|
||||
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
assert.False(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares.String(), leftBonded)
|
||||
|
||||
// should be able to unbond just what we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()))
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares = sdk.NewRat(leftBonded)
|
||||
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares, leftBonded)
|
||||
}
|
||||
|
||||
func TestMultipleMsgCreateValidator(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
validatorAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
validatorAddrs := []sdk.Address{keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]}
|
||||
|
||||
// bond them all
|
||||
for i, validatorAddr := range validatorAddrs {
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[i], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[i], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
|
@ -305,7 +320,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
validators := keeper.GetValidators(ctx, 100)
|
||||
require.Equal(t, (i + 1), len(validators))
|
||||
val := validators[i]
|
||||
balanceExpd := initBond.SubRaw(10)
|
||||
balanceExpd := sdk.NewInt(initBond - 10)
|
||||
balanceGot := accMapper.GetAccount(ctx, val.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators)
|
||||
require.Equal(t, 10, int(val.DelegatorShares.Evaluate()), "expected %d shares, got %d", 10, val.DelegatorShares)
|
||||
|
@ -316,8 +331,11 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
for i, validatorAddr := range validatorAddrs {
|
||||
validatorPre, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "10") // self-delegation
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) // self-delegation
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
|
@ -328,24 +346,25 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
expBalance := initBond
|
||||
expBalance := sdk.NewInt(initBond)
|
||||
gotBalance := accMapper.GetAccount(ctx, validatorPre.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleMsgDelegate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
validatorAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, delegatorAddrs := keep.Addrs[0], keep.Addrs[1:]
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
//first make a validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
|
||||
// delegate multiple parties
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
|
@ -357,8 +376,11 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
|
||||
// unbond them all
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
|
@ -368,37 +390,173 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRevokeValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// bond a delegator
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||
|
||||
validator, _ := keeper.GetValidator(ctx, validatorAddr)
|
||||
|
||||
// unbond the validators bond portion
|
||||
msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.True(t, validator.Revoked)
|
||||
require.True(t, validator.Revoked, "%v", validator)
|
||||
|
||||
// test that this address cannot yet be bonded too because is revoked
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
assert.False(t, got.IsOK(), "expected error, got %v", got)
|
||||
|
||||
// test that the delegator can still withdraw their bonds
|
||||
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// verify that the pubkey can now be reused
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||
}
|
||||
|
||||
func TestUnbondingPeriod(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr := keep.Addrs[0]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 7
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin unbonding
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// cannot complete unbonding at same time
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, !got.IsOK(), "expected no error")
|
||||
|
||||
// cannot complete unbonding at time 6 seconds later
|
||||
origHeader := ctx.BlockHeader()
|
||||
headerTime6 := origHeader
|
||||
headerTime6.Time += 6
|
||||
ctx = ctx.WithBlockHeader(headerTime6)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, !got.IsOK(), "expected no error")
|
||||
|
||||
// can complete unbonding at time 7 seconds later
|
||||
headerTime7 := origHeader
|
||||
headerTime7.Time += 7
|
||||
ctx = ctx.WithBlockHeader(headerTime7)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
||||
func TestRedelegationPeriod(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, validatorAddr2 := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 7
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// cannot complete redelegation at same time
|
||||
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error")
|
||||
|
||||
// cannot complete redelegation at time 6 seconds later
|
||||
origHeader := ctx.BlockHeader()
|
||||
headerTime6 := origHeader
|
||||
headerTime6.Time += 6
|
||||
ctx = ctx.WithBlockHeader(headerTime6)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error")
|
||||
|
||||
// can complete redelegation at time 7 seconds later
|
||||
headerTime7 := origHeader
|
||||
headerTime7.Time += 7
|
||||
ctx = ctx.WithBlockHeader(headerTime7)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
||||
func TestTransitiveRedelegation(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, validatorAddr2, validatorAddr3 := keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 0
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// cannot redelegation to next validator while first delegation exists
|
||||
msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
|
||||
|
||||
// complete first redelegation
|
||||
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// now should be able to redelegate from the second validator to the third
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
|
|
@ -1,859 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// keeper of the staking store
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.Keeper
|
||||
|
||||
// codespace
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
keeper := Keeper{
|
||||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
codespace: codespace,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// get a single validator
|
||||
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return k.getValidator(store, addr)
|
||||
}
|
||||
|
||||
// get a single validator by pubkey
|
||||
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
|
||||
if addr == nil {
|
||||
return validator, false
|
||||
}
|
||||
return k.getValidator(store, addr)
|
||||
}
|
||||
|
||||
// get a single validator (reuse store)
|
||||
func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) {
|
||||
b := store.Get(GetValidatorKey(addr))
|
||||
if b == nil {
|
||||
return validator, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &validator)
|
||||
return validator, true
|
||||
}
|
||||
|
||||
// set the main record holding validator details
|
||||
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set main store
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bz)
|
||||
}
|
||||
|
||||
func (k Keeper) setValidatorByPubKeyIndex(ctx sdk.Context, validator Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set pointer by pubkey
|
||||
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
|
||||
}
|
||||
|
||||
func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner)
|
||||
}
|
||||
|
||||
// used in testing
|
||||
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
||||
|
||||
// Get the set of all validators with no limits, used during genesis dump
|
||||
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators = append(validators, validator)
|
||||
iterator.Next()
|
||||
}
|
||||
return validators
|
||||
}
|
||||
|
||||
// Get the set of all validators, retrieve a maxRetrieve number of records
|
||||
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators Validators) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
validators = make([]Validator, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators[i] = validator
|
||||
iterator.Next()
|
||||
}
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// get the group of the bonded validators
|
||||
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators = make([]Validator, maxValidators)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
|
||||
i := 0
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
// sanity check
|
||||
if i > int(maxValidators-1) {
|
||||
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// get the group of bonded validators sorted by power-rank
|
||||
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators := make([]Validator, maxValidators)
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
|
||||
i := 0
|
||||
for {
|
||||
if !iterator.Valid() || i > int(maxValidators-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
// Reached to revoked validators, stop iterating
|
||||
if validator.Revoked {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
if validator.Status() == sdk.Bonded {
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// Accumulated updates to the active/bonded validator set for tendermint
|
||||
|
||||
// get the most recently updated validators
|
||||
func (k Keeper) getTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val abci.Validator
|
||||
k.cdc.MustUnmarshalBinary(valBytes, &val)
|
||||
updates = append(updates, val)
|
||||
}
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries after applied to Tendermint
|
||||
func (k Keeper) clearTendermintUpdates(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// delete subspace
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// perfom all the nessisary steps for when a validator changes its power
|
||||
// updates all validator stores as well as tendermint update store
|
||||
// may kick out validators if new validator is entering the bonded validator group
|
||||
func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.getPool(store)
|
||||
ownerAddr := validator.Owner
|
||||
|
||||
// always update the main list ordered by owner address before exiting
|
||||
defer func() {
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(ownerAddr), bz)
|
||||
}()
|
||||
|
||||
// retrieve the old validator record
|
||||
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
|
||||
|
||||
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
validator = k.unbondValidator(ctx, store, validator)
|
||||
|
||||
// need to also clear the cliff validator spot because the revoke has
|
||||
// opened up a new spot which will be filled when
|
||||
// updateValidatorsBonded is called
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
|
||||
powerIncreasing := false
|
||||
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
|
||||
powerIncreasing = true
|
||||
}
|
||||
|
||||
// if already a validator, copy the old block height and counter, else set them
|
||||
if oldFound && oldValidator.Status() == sdk.Bonded {
|
||||
validator.BondHeight = oldValidator.BondHeight
|
||||
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
|
||||
} else {
|
||||
validator.BondHeight = ctx.BlockHeight()
|
||||
counter := k.getIntraTxCounter(ctx)
|
||||
validator.BondIntraTxCounter = counter
|
||||
k.setIntraTxCounter(ctx, counter+1)
|
||||
}
|
||||
|
||||
// update the list ordered by voting power
|
||||
if oldFound {
|
||||
store.Delete(GetValidatorsByPowerKey(oldValidator, pool))
|
||||
}
|
||||
valPower := GetValidatorsByPowerKey(validator, pool)
|
||||
store.Set(valPower, validator.Owner)
|
||||
|
||||
// efficiency case:
|
||||
// if already bonded and power increasing only need to update tendermint
|
||||
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
bz := k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
|
||||
return validator
|
||||
}
|
||||
|
||||
// efficiency case:
|
||||
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
|
||||
cliffPower := k.getCliffValidatorPower(ctx)
|
||||
if cliffPower != nil &&
|
||||
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
|
||||
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
|
||||
return validator
|
||||
}
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal := k.updateBondedValidators(ctx, store, validator)
|
||||
if updatedVal.Owner != nil { // updates to validator occurred to be updated
|
||||
validator = updatedVal
|
||||
}
|
||||
return validator
|
||||
}
|
||||
|
||||
// Update the validator group and kick out any old validators. In addition this
|
||||
// function adds (or doesn't add) a validator which has updated its bonded
|
||||
// tokens to the validator group. -> this validator is specified through the
|
||||
// updatedValidatorAddr term.
|
||||
//
|
||||
// The correct subset is retrieved by iterating through an index of the
|
||||
// validators sorted by power, stored using the ValidatorsByPowerKey.
|
||||
// Simultaneously the current validator records are updated in store with the
|
||||
// ValidatorsBondedKey. This store is used to determine if a validator is a
|
||||
// validator without needing to iterate over the subspace as we do in
|
||||
// GetValidators.
|
||||
//
|
||||
// Optionally also return the validator from a retrieve address if the validator has been bonded
|
||||
func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore,
|
||||
newValidator Validator) (updatedVal Validator) {
|
||||
|
||||
kickCliffValidator := false
|
||||
oldCliffValidatorAddr := k.getCliffValidator(ctx)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
// TODO benchmark if we should read the current power and not write if it's the same
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store, or under the
|
||||
// situation that this is the "new validator" just use the validator
|
||||
// provided because it has not yet been updated in the main validator
|
||||
// store
|
||||
ownerAddr := iterator.Value()
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
validator = newValidator
|
||||
} else {
|
||||
var found bool
|
||||
validator, found = k.getValidator(store, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
}
|
||||
|
||||
// if not previously a validator (and unrevoked),
|
||||
// kick the cliff validator / bond this new validator
|
||||
if validator.Status() != sdk.Bonded && !validator.Revoked {
|
||||
kickCliffValidator = true
|
||||
|
||||
validator = k.bondValidator(ctx, store, validator)
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
updatedVal = validator
|
||||
}
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
// perform the actual kicks
|
||||
if oldCliffValidatorAddr != nil && kickCliffValidator {
|
||||
validator, found := k.getValidator(store, oldCliffValidatorAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, store, validator)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// full update of the bonded validator set, many can be added/kicked
|
||||
func (k Keeper) updateBondedValidatorsFull(ctx sdk.Context, store sdk.KVStore) {
|
||||
// clear the current validators store, add to the ToKickOut temp store
|
||||
toKickOut := make(map[string]byte)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
ownerAddr := iterator.Value()
|
||||
toKickOut[string(ownerAddr)] = 0 // set anything
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store,
|
||||
// or under the situation that this is the "new validator" just
|
||||
// use the validator provided because it has not yet been updated
|
||||
// in the main validator store
|
||||
ownerAddr := iterator.Value()
|
||||
var found bool
|
||||
validator, found = k.getValidator(store, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
|
||||
_, found = toKickOut[string(ownerAddr)]
|
||||
if found {
|
||||
delete(toKickOut, string(ownerAddr))
|
||||
} else {
|
||||
|
||||
// if it wasn't in the toKickOut group it means
|
||||
// this wasn't a previously a validator, therefor
|
||||
// update the validator to enter the validator group
|
||||
validator = k.bondValidator(ctx, store, validator)
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
// perform the actual kicks
|
||||
for key := range toKickOut {
|
||||
ownerAddr := []byte(key)
|
||||
validator, found := k.getValidator(store, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, store, validator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes unbonded
|
||||
func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator {
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Unbonded {
|
||||
panic(fmt.Sprintf("should not already be be unbonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||
k.setPool(ctx, pool)
|
||||
|
||||
// save the now unbonded validator record
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
// also remove from the Bonded Validators Store
|
||||
store.Delete(GetValidatorsBondedKey(validator.PubKey))
|
||||
return validator
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes bonded
|
||||
func (k Keeper) bondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator {
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("should not already be be bonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||
k.setPool(ctx, pool)
|
||||
|
||||
// save the now bonded validator record to the three referenced stores
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
func (k Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
|
||||
|
||||
// first retrieve the old validator record
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
// delete the old validator record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.getPool(store)
|
||||
store.Delete(GetValidatorKey(address))
|
||||
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
|
||||
store.Delete(GetValidatorsByPowerKey(validator, pool))
|
||||
|
||||
// delete from the current and power weighted validator groups if the validator
|
||||
// is bonded - and add validator with zero power to the validator updates
|
||||
if store.Get(GetValidatorsBondedKey(validator.PubKey)) == nil {
|
||||
return
|
||||
}
|
||||
store.Delete(GetValidatorsBondedKey(validator.PubKey))
|
||||
|
||||
bz := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(address), bz)
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// load a delegator bond
|
||||
func (k Keeper) GetDelegation(ctx sdk.Context,
|
||||
delegatorAddr, validatorAddr sdk.Address) (bond Delegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, validatorAddr, k.cdc))
|
||||
if delegatorBytes == nil {
|
||||
return bond, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(delegatorBytes, &bond)
|
||||
return bond, true
|
||||
}
|
||||
|
||||
// load all delegations used during genesis dump
|
||||
func (k Keeper) getAllDelegations(ctx sdk.Context) (delegations []Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var delegation Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
|
||||
delegations = append(delegations, delegation)
|
||||
iterator.Next()
|
||||
}
|
||||
return delegations[:i] // trim
|
||||
}
|
||||
|
||||
// load all bonds of a delegator
|
||||
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest
|
||||
|
||||
bonds = make([]Delegation, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var bond Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &bond)
|
||||
bonds[i] = bond
|
||||
iterator.Next()
|
||||
}
|
||||
return bonds[:i] // trim
|
||||
}
|
||||
|
||||
func (k Keeper) setDelegation(ctx sdk.Context, bond Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(bond)
|
||||
store.Set(GetDelegationKey(bond.DelegatorAddr, bond.ValidatorAddr, k.cdc), b)
|
||||
}
|
||||
|
||||
func (k Keeper) removeDelegation(ctx sdk.Context, bond Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(GetDelegationKey(bond.DelegatorAddr, bond.ValidatorAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the global staking params
|
||||
func (k Keeper) GetParams(ctx sdk.Context) Params {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return k.getParams(store)
|
||||
}
|
||||
func (k Keeper) getParams(store sdk.KVStore) (params Params) {
|
||||
b := store.Get(ParamKey)
|
||||
if b == nil {
|
||||
panic("Stored params should not have been nil")
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(b, ¶ms)
|
||||
return
|
||||
}
|
||||
|
||||
// Need a distinct function because setParams depends on an existing previous
|
||||
// record of params to exist (to check if maxValidators has changed) - and we
|
||||
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
|
||||
// first params set it will panic.
|
||||
func (k Keeper) setNewParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
// Public version of setNewParams
|
||||
func (k Keeper) SetNewParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
func (k Keeper) setParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
exParams := k.getParams(store)
|
||||
|
||||
// if max validator count changes, must recalculate validator set
|
||||
if exParams.MaxValidators != params.MaxValidators {
|
||||
k.updateBondedValidatorsFull(ctx, store)
|
||||
}
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the pool
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return k.getPool(store)
|
||||
}
|
||||
func (k Keeper) getPool(store sdk.KVStore) (pool Pool) {
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
panic("Stored pool should not have been nil")
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &pool)
|
||||
return
|
||||
}
|
||||
|
||||
func (k Keeper) setPool(ctx sdk.Context, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
// Public version of setpool
|
||||
func (k Keeper) SetPool(ctx sdk.Context, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) getIntraTxCounter(ctx sdk.Context) int16 {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
var counter int16
|
||||
k.cdc.MustUnmarshalBinary(b, &counter)
|
||||
return counter
|
||||
}
|
||||
|
||||
// set the current in-block validator operation counter
|
||||
func (k Keeper) setIntraTxCounter(ctx sdk.Context, counter int16) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(counter)
|
||||
store.Set(IntraTxCounterKey, bz)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current validator on the cliff
|
||||
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorCliffKey)
|
||||
}
|
||||
|
||||
// get the current power of the validator on the cliff
|
||||
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorPowerCliffKey)
|
||||
}
|
||||
|
||||
// set the current validator and power of the validator on the cliff
|
||||
func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := GetValidatorsByPowerKey(validator, pool)
|
||||
store.Set(ValidatorPowerCliffKey, bz)
|
||||
store.Set(ValidatorCliffKey, validator.Owner)
|
||||
}
|
||||
|
||||
// clear the current validator and power of the validator on the cliff
|
||||
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(ValidatorPowerCliffKey)
|
||||
store.Delete(ValidatorCliffKey)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements ValidatorSet
|
||||
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// get the sdk.validator for a particular address
|
||||
func (k Keeper) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
|
||||
val, found := k.GetValidator(ctx, addr)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// total power from the bond
|
||||
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
||||
pool := k.GetPool(ctx)
|
||||
return pool.BondedShares
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements DelegationSet
|
||||
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// get the delegation for a particular set of delegator and validator addresses
|
||||
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
|
||||
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
// Returns self as it is both a validatorset and delegationset
|
||||
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
|
||||
return k
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := GetDelegationsKey(delAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, key)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var delegation Delegation
|
||||
k.cdc.MustUnmarshalBinary(bz, &delegation)
|
||||
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// slash a validator
|
||||
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
|
||||
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("attempted to slash a nonexistent validator with address %s", pubkey.Address()))
|
||||
}
|
||||
sharesToRemove := val.PoolShares.Amount.Mul(fraction)
|
||||
pool := k.GetPool(ctx)
|
||||
val, pool, burned := val.removePoolShares(pool, sharesToRemove)
|
||||
k.setPool(ctx, pool) // update the pool
|
||||
k.updateValidator(ctx, val) // update the validator, possibly kicking it out
|
||||
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %v tokens", pubkey.Address(), fraction, sharesToRemove, burned))
|
||||
return
|
||||
}
|
||||
|
||||
// revoke a validator
|
||||
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("validator with pubkey %s not found, cannot revoke", pubkey))
|
||||
}
|
||||
val.Revoked = true
|
||||
k.updateValidator(ctx, val) // update the validator, now revoked
|
||||
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// unrevoke a validator
|
||||
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("validator with pubkey %s not found, cannot unrevoke", pubkey))
|
||||
}
|
||||
val.Revoked = false
|
||||
k.updateValidator(ctx, val) // update the validator, now unrevoked
|
||||
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
|
||||
return
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// load a delegation
|
||||
func (k Keeper) GetDelegation(ctx sdk.Context,
|
||||
delegatorAddr, validatorAddr sdk.Address) (delegation types.Delegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, validatorAddr, k.cdc))
|
||||
if delegatorBytes == nil {
|
||||
return delegation, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(delegatorBytes, &delegation)
|
||||
return delegation, true
|
||||
}
|
||||
|
||||
// load all delegations used during genesis dump
|
||||
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
|
||||
delegations = append(delegations, delegation)
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return delegations
|
||||
}
|
||||
|
||||
// load all delegations for a delegator
|
||||
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address,
|
||||
maxRetrieve int16) (delegations []types.Delegation) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest
|
||||
|
||||
delegations = make([]types.Delegation, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
|
||||
delegations[i] = delegation
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return delegations[:i] // trim
|
||||
}
|
||||
|
||||
// set the delegation
|
||||
func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(delegation)
|
||||
store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc), b)
|
||||
}
|
||||
|
||||
// remove the delegation
|
||||
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// load a unbonding delegation
|
||||
func (k Keeper) GetUnbondingDelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorAddr sdk.Address) (ubd types.UnbondingDelegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
ubdKey := GetUBDKey(DelegatorAddr, ValidatorAddr, k.cdc)
|
||||
bz := store.Get(ubdKey)
|
||||
if bz == nil {
|
||||
return ubd, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(bz, &ubd)
|
||||
return ubd, true
|
||||
}
|
||||
|
||||
// set the unbonding delegation and associated index
|
||||
func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(ubd)
|
||||
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
|
||||
store.Set(ubdKey, bz)
|
||||
store.Set(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc), ubdKey)
|
||||
}
|
||||
|
||||
// remove the unbonding delegation object and associated index
|
||||
func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
|
||||
store.Delete(ubdKey)
|
||||
store.Delete(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// load a redelegation
|
||||
func (k Keeper) GetRedelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr sdk.Address) (red types.Redelegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
redKey := GetREDKey(DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr, k.cdc)
|
||||
bz := store.Get(redKey)
|
||||
if bz == nil {
|
||||
return red, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(bz, &red)
|
||||
return red, true
|
||||
}
|
||||
|
||||
// has a redelegation
|
||||
func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorDstAddr sdk.Address) bool {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
prefix := GetREDsByDelToValDstIndexKey(DelegatorAddr, ValidatorDstAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, prefix) //smallest to largest
|
||||
|
||||
found := false
|
||||
if iterator.Valid() {
|
||||
//record found
|
||||
found = true
|
||||
}
|
||||
iterator.Close()
|
||||
return found
|
||||
}
|
||||
|
||||
// set a redelegation and associated index
|
||||
func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(red)
|
||||
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
|
||||
store.Set(redKey, bz)
|
||||
store.Set(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
|
||||
store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
|
||||
}
|
||||
|
||||
// remove a redelegation object and associated index
|
||||
func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
|
||||
store.Delete(redKey)
|
||||
store.Delete(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
|
||||
store.Delete(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// Perform a delegation, set/update everything necessary within the store
|
||||
func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.Address, bondAmt sdk.Coin,
|
||||
validator types.Validator) (newShares sdk.Rat, err sdk.Error) {
|
||||
|
||||
// Get or create the delegator delegation
|
||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
if !found {
|
||||
delegation = types.Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
pool := k.GetPool(ctx)
|
||||
_, _, err = k.coinKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount.Int64())
|
||||
delegation.Shares = delegation.Shares.Add(newShares)
|
||||
|
||||
// Update delegation height
|
||||
delegation.Height = ctx.BlockHeight()
|
||||
|
||||
k.SetPool(ctx, pool)
|
||||
k.SetDelegation(ctx, delegation)
|
||||
k.UpdateValidator(ctx, validator)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// unbond the the delegation return
|
||||
func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address,
|
||||
shares sdk.Rat) (amount int64, err sdk.Error) {
|
||||
|
||||
// check if delegation has any shares in it unbond
|
||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
if !found {
|
||||
err = types.ErrNoDelegatorForAddress(k.Codespace())
|
||||
return
|
||||
}
|
||||
|
||||
// retrieve the amount to remove
|
||||
if delegation.Shares.LT(shares) {
|
||||
err = types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())
|
||||
return
|
||||
}
|
||||
|
||||
// get validator
|
||||
validator, found := k.GetValidator(ctx, validatorAddr)
|
||||
if !found {
|
||||
err = types.ErrNoValidatorFound(k.Codespace())
|
||||
return
|
||||
}
|
||||
|
||||
// subtract shares from delegator
|
||||
delegation.Shares = delegation.Shares.Sub(shares)
|
||||
|
||||
// remove the delegation
|
||||
if delegation.Shares.IsZero() {
|
||||
|
||||
// if the delegation is the owner of the validator then
|
||||
// trigger a revoke validator
|
||||
if bytes.Equal(delegation.DelegatorAddr, validator.Owner) && validator.Revoked == false {
|
||||
validator.Revoked = true
|
||||
}
|
||||
k.RemoveDelegation(ctx, delegation)
|
||||
} else {
|
||||
// Update height
|
||||
delegation.Height = ctx.BlockHeight()
|
||||
k.SetDelegation(ctx, delegation)
|
||||
}
|
||||
|
||||
// remove the coins from the validator
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, amount = validator.RemoveDelShares(pool, shares)
|
||||
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// update then remove validator if necessary
|
||||
validator = k.UpdateValidator(ctx, validator)
|
||||
if validator.DelegatorShares.IsZero() {
|
||||
k.RemoveValidator(ctx, validator.Owner)
|
||||
}
|
||||
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the unbonding delegation
|
||||
params := k.GetParams(ctx)
|
||||
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
MinTime: minTime,
|
||||
Balance: sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)},
|
||||
}
|
||||
k.SetUnbondingDelegation(ctx, ubd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address) sdk.Error {
|
||||
|
||||
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
if !found {
|
||||
return types.ErrNoUnbondingDelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// ensure that enough time has passed
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if ubd.MinTime > ctxTime {
|
||||
return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime)
|
||||
}
|
||||
|
||||
k.coinKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{ubd.Balance})
|
||||
k.RemoveUnbondingDelegation(ctx, ubd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
|
||||
|
||||
// check if this is a transitive redelegation
|
||||
if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) {
|
||||
return types.ErrTransitiveRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorSrcAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := k.GetParams(ctx)
|
||||
returnCoin := sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)}
|
||||
dstValidator, found := k.GetValidator(ctx, validatorDstAddr)
|
||||
if !found {
|
||||
return types.ErrBadRedelegationDst(k.Codespace())
|
||||
}
|
||||
sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator)
|
||||
|
||||
// create the unbonding delegation
|
||||
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
||||
|
||||
red := types.Redelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
MinTime: minTime,
|
||||
SharesDst: sharesCreated,
|
||||
SharesSrc: sharesAmount,
|
||||
}
|
||||
k.SetRedelegation(ctx, red)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an ongoing redelegation
|
||||
func (k Keeper) CompleteRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address) sdk.Error {
|
||||
|
||||
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
||||
if !found {
|
||||
return types.ErrNoRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// ensure that enough time has passed
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if red.MinTime > ctxTime {
|
||||
return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime)
|
||||
}
|
||||
|
||||
k.RemoveRedelegation(ctx, red)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// tests GetDelegation, GetDelegations, SetDelegation, RemoveDelegation, GetDelegations
|
||||
func TestDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 10)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
amts := []int64{9, 8, 7}
|
||||
var validators [3]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
}
|
||||
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
|
||||
// first add a validators[0] to delegate too
|
||||
bond1to1 := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetDelegation(ctx, bond1to1)
|
||||
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.Equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.SetDelegation(ctx, bond1to1)
|
||||
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.Equal(resBond))
|
||||
|
||||
// add some more records
|
||||
bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.SetDelegation(ctx, bond1to2)
|
||||
keeper.SetDelegation(ctx, bond1to3)
|
||||
keeper.SetDelegation(ctx, bond2to1)
|
||||
keeper.SetDelegation(ctx, bond2to2)
|
||||
keeper.SetDelegation(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond1to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond1to2.Equal(resBonds[1]))
|
||||
assert.True(t, bond1to3.Equal(resBonds[2]))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond2to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.Equal(resBonds[1]))
|
||||
assert.True(t, bond2to3.Equal(resBonds[2]))
|
||||
allBonds := keeper.GetAllDelegations(ctx)
|
||||
require.Equal(t, 6, len(allBonds))
|
||||
assert.True(t, bond1to1.Equal(allBonds[0]))
|
||||
assert.True(t, bond1to2.Equal(allBonds[1]))
|
||||
assert.True(t, bond1to3.Equal(allBonds[2]))
|
||||
assert.True(t, bond2to1.Equal(allBonds[3]))
|
||||
assert.True(t, bond2to2.Equal(allBonds[4]))
|
||||
assert.True(t, bond2to3.Equal(allBonds[5]))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveDelegation(ctx, bond2to3)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
assert.True(t, bond2to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.Equal(resBonds[1]))
|
||||
|
||||
// delete all the records from delegator 2
|
||||
keeper.RemoveDelegation(ctx, bond2to1)
|
||||
keeper.RemoveDelegation(ctx, bond2to2)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
|
||||
assert.False(t, found)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 0, len(resBonds))
|
||||
}
|
||||
|
||||
// tests Get/Set/Remove UnbondingDelegation
|
||||
func TestUnbondingDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 0,
|
||||
MinTime: 0,
|
||||
Balance: sdk.NewCoin("steak", 5),
|
||||
}
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
resBond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, ubd.Equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
ubd.Balance = sdk.NewCoin("steak", 21)
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
resBond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, ubd.Equal(resBond))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveUnbondingDelegation(ctx, ubd)
|
||||
_, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
func TestUnbondDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = 10
|
||||
|
||||
//create a validator and a delegator to that validator
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, 10)
|
||||
require.Equal(t, int64(10), issuedShares.Evaluate())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
require.Equal(t, int64(10), pool.BondedTokens)
|
||||
require.Equal(t, int64(10), validator.PoolShares.Bonded().Evaluate())
|
||||
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
var err error
|
||||
var amount int64
|
||||
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(6), amount) // shares to be added to an unbonding delegation / redelegation
|
||||
|
||||
delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
||||
assert.Equal(t, int64(4), delegation.Shares.Evaluate())
|
||||
assert.Equal(t, int64(4), validator.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(6), pool.LooseTokens, "%v", pool)
|
||||
assert.Equal(t, int64(4), pool.BondedTokens)
|
||||
}
|
||||
|
||||
// tests Get/Set/Remove/Has UnbondingDelegation
|
||||
func TestRedelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 0,
|
||||
MinTime: 0,
|
||||
SharesSrc: sdk.NewRat(5),
|
||||
SharesDst: sdk.NewRat(5),
|
||||
}
|
||||
|
||||
// test shouldn't have and redelegations
|
||||
has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
|
||||
assert.False(t, has)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.True(t, found)
|
||||
assert.True(t, rd.Equal(resBond))
|
||||
|
||||
// check if has the redelegation
|
||||
has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
|
||||
assert.True(t, has)
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
rd.SharesSrc = sdk.NewRat(21)
|
||||
rd.SharesDst = sdk.NewRat(21)
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.True(t, found)
|
||||
assert.True(t, rd.Equal(resBond))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveRedelegation(ctx, rd)
|
||||
_, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
|
@ -1,35 +1,32 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
const (
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 100000000000 // increased to this precision for accuracy with tests on tick_test.go
|
||||
precision = 100000000000 // increased to this precision for accuracy
|
||||
)
|
||||
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr)
|
||||
|
||||
// process provisions for an hour period
|
||||
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
||||
func (k Keeper) ProcessProvisions(ctx sdk.Context) types.Pool {
|
||||
|
||||
pool := k.GetPool(ctx)
|
||||
pool.Inflation = k.nextInflation(ctx)
|
||||
pool.Inflation = k.NextInflation(ctx)
|
||||
|
||||
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
|
||||
// more bonded tokens are added proportionally to all validators the only term
|
||||
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
|
||||
|
||||
provisions := pool.Inflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat).EvaluateInt()
|
||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate()
|
||||
|
||||
// TODO add to the fees provisions
|
||||
pool.LooseUnbondedTokens = pool.LooseUnbondedTokens.Add(provisions)
|
||||
pool.LooseTokens += provisions
|
||||
return pool
|
||||
}
|
||||
|
||||
// get the next inflation rate for the hour
|
||||
func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
||||
func (k Keeper) NextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
||||
|
||||
params := k.GetParams(ctx)
|
||||
pool := k.GetPool(ctx)
|
||||
|
@ -40,7 +37,7 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
|||
// 7% and 20%.
|
||||
|
||||
// (1 - bondedRatio/GoalBonded) * InflationRateChange
|
||||
inflationRateChangePerYear := sdk.OneRat().Sub(pool.bondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
||||
inflationRateChangePerYear := sdk.OneRat().Sub(pool.BondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
||||
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat)
|
||||
|
||||
// increase the new annual inflation for this next cycle
|
|
@ -1,65 +1,64 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
|
||||
var r = rand.New(rand.NewSource(6595))
|
||||
|
||||
func TestGetInflation(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedTokens / TotalSupply
|
||||
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
zero := sdk.ZeroInt()
|
||||
one := sdk.OneInt()
|
||||
// BondedRatio = BondedTokens / TotalSupply
|
||||
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setBondedTokens, setLooseTokens sdk.Int
|
||||
setBondedTokens, setLooseTokens int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
}{
|
||||
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||
{"test 1", zero, zero, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 100% bonded, starting at 20% inflation and being reduced
|
||||
// (1 - (1/0.67))*(0.13/8667)
|
||||
{"test 2", one, zero, sdk.NewRat(20, 100),
|
||||
{"test 2", 1, 0, sdk.NewRat(20, 100),
|
||||
sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 50% bonded, starting at 10% inflation and being increased
|
||||
{"test 3", one, one, sdk.NewRat(10, 100),
|
||||
{"test 3", 1, 1, sdk.NewRat(10, 100),
|
||||
sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// test 7% minimum stop (testing with 100% bonded)
|
||||
{"test 4", one, zero, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", one, zero, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
{"test 4", 1, 0, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", 1, 0, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
|
||||
// test 20% maximum stop (testing with 0% bonded)
|
||||
{"test 6", zero, zero, sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||
{"test 7", zero, zero, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
|
||||
// perfect balance shouldn't change inflation
|
||||
{"test 8", sdk.NewInt(67), sdk.NewInt(33), sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
{"test 8", 67, 33, sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
pool.BondedTokens, pool.LooseUnbondedTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||
pool.Inflation = tc.setInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
inflation := keeper.nextInflation(ctx)
|
||||
inflation := keeper.NextInflation(ctx)
|
||||
diffInflation := inflation.Sub(tc.setInflation)
|
||||
|
||||
assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
|
@ -69,17 +68,18 @@ func TestGetInflation(t *testing.T) {
|
|||
|
||||
// Test that provisions are correctly added to the pool and validators each hour for 1 year
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 250000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 2
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 250000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 2
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
|
@ -89,7 +89,7 @@ func TestProcessProvisions(t *testing.T) {
|
|||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
_, expProvisions, _ := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
}
|
||||
|
||||
//get the pool and do the final value checks from checkFinalPoolValues
|
||||
|
@ -100,17 +100,18 @@ func TestProcessProvisions(t *testing.T) {
|
|||
// Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate
|
||||
// Cycles through the whole gambit of inflation possibilities, starting at 7% inflation, up to 20%, back down to 7% (it takes ~11.4 years)
|
||||
func TestHourlyInflationRateOfChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 150000000
|
||||
initialUnbondedTokens int64 = 400000000
|
||||
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 1
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 150000000
|
||||
initialUnbondedTokens int64 = 400000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
|
@ -121,7 +122,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
|
|||
pool := keeper.GetPool(ctx)
|
||||
previousInflation := pool.Inflation
|
||||
updatedInflation, expProvisions, pool := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
msg := strconv.Itoa(hr)
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
}
|
||||
|
@ -133,7 +134,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
|
|||
|
||||
//Test that a large unbonding will significantly lower the bonded ratio
|
||||
func TestLargeUnbond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
|
@ -147,32 +148,33 @@ func TestLargeUnbond(t *testing.T) {
|
|||
validatorTokens = []int64{300000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 7
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[0])
|
||||
validator, found := keeper.GetValidator(ctx, Addrs[0])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
initialBondedRatio := pool.BondedRatio()
|
||||
|
||||
// validator[0] will be unbonded, bringing us from 75% bonded ratio to ~50% (unbonding 300,000,000)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
|
||||
bondedShares = bondedShares.Sub(bondSharesVal0)
|
||||
val0UnbondedTokens = pool.unbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
|
||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.unbondedShareExRate()))
|
||||
val0UnbondedTokens = pool.UnbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
|
||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.UnbondedShareExRate()))
|
||||
|
||||
// unbonded shares should increase
|
||||
assert.True(t, unbondedShares.GT(sdk.NewRat(300000000, 1)))
|
||||
// Ensure that new bonded ratio is less than old bonded ratio , because before they were increasing (i.e. 50% < 75)
|
||||
assert.True(t, (pool.bondedRatio().LT(initialBondedRatio)))
|
||||
assert.True(t, (pool.BondedRatio().LT(initialBondedRatio)))
|
||||
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
@ -181,7 +183,7 @@ func TestLargeUnbond(t *testing.T) {
|
|||
|
||||
//Test that a large bonding will significantly increase the bonded ratio
|
||||
func TestLargeBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
|
@ -193,24 +195,25 @@ func TestLargeBond(t *testing.T) {
|
|||
validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[9])
|
||||
validator, found := keeper.GetValidator(ctx, Addrs[9])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
initialBondedRatio := pool.BondedRatio()
|
||||
|
||||
params := DefaultParams()
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = bondedValidators + 1 //must do this to allow for an extra validator to bond
|
||||
keeper.setParams(ctx, params)
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// validator[9] will be bonded, bringing us from 25% to ~50% (bonding 400,000,000 tokens)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
|
@ -219,7 +222,7 @@ func TestLargeBond(t *testing.T) {
|
|||
// unbonded shares should decrease
|
||||
assert.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1)))
|
||||
// Ensure that new bonded ratio is greater than old bonded ratio (i.e. 50% > 25%)
|
||||
assert.True(t, (pool.bondedRatio().GT(initialBondedRatio)))
|
||||
assert.True(t, (pool.BondedRatio().GT(initialBondedRatio)))
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
||||
|
@ -228,20 +231,19 @@ func TestLargeBond(t *testing.T) {
|
|||
|
||||
// Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators
|
||||
func TestInflationWithRandomOperations(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
params := DefaultParams()
|
||||
keeper.setParams(ctx, params)
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
params := types.DefaultParams()
|
||||
keeper.SetParams(ctx, params)
|
||||
numValidators := 20
|
||||
|
||||
// start off by randomly setting up 20 validators
|
||||
pool, validators := randomSetup(r, numValidators)
|
||||
pool, validators := types.RandomSetup(r, numValidators)
|
||||
require.Equal(t, numValidators, len(validators))
|
||||
|
||||
for i := 0; i < len(validators); i++ {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
keeper.SetValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// Used to rotate validators so each random operation is applied to a different validator
|
||||
validatorCounter := 0
|
||||
|
@ -250,30 +252,30 @@ func TestInflationWithRandomOperations(t *testing.T) {
|
|||
for i := 0; i < numValidators; i++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// Get inflation before randomOperation, for comparison later
|
||||
// Get inflation before RandomOperation, for comparison later
|
||||
previousInflation := pool.Inflation
|
||||
|
||||
// Perform the random operation, and record how validators are modified
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, pool, validators[validatorCounter])
|
||||
validatorsMod := make([]Validator, len(validators))
|
||||
poolMod, validatorMod, tokens, msg := types.RandomOperation(r)(r, pool, validators[validatorCounter])
|
||||
validatorsMod := make([]types.Validator, len(validators))
|
||||
copy(validatorsMod[:], validators[:])
|
||||
require.Equal(t, numValidators, len(validators), "i %v", validatorCounter)
|
||||
require.Equal(t, numValidators, len(validatorsMod), "i %v", validatorCounter)
|
||||
validatorsMod[validatorCounter] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
types.AssertInvariants(t, msg,
|
||||
pool, validators,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
// set pool and validators after the random operation
|
||||
pool = poolMod
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators = validatorsMod
|
||||
|
||||
// Must set inflation here manually, as opposed to most other tests in this suite, where we call keeper.processProvisions(), which updates pool.Inflation
|
||||
updatedInflation := keeper.nextInflation(ctx)
|
||||
updatedInflation := keeper.NextInflation(ctx)
|
||||
pool.Inflation = updatedInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// Ensure inflation changes as expected when random operations are applied.
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
|
@ -285,41 +287,43 @@ func TestInflationWithRandomOperations(t *testing.T) {
|
|||
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
|
||||
|
||||
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
|
||||
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens int64, cumulativeExpProvs sdk.Int) {
|
||||
calculatedTotalTokens := cumulativeExpProvs.AddRaw(initialTotalTokens)
|
||||
|
||||
assert.Equal(t, calculatedTotalTokens.Int64(), pool.TokenSupply().Int64())
|
||||
func checkFinalPoolValues(t *testing.T, pool types.Pool, initialTotalTokens, cumulativeExpProvs int64) {
|
||||
calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs
|
||||
assert.Equal(t, calculatedTotalTokens, pool.TokenSupply())
|
||||
}
|
||||
|
||||
// Processes provisions are added to the pool correctly every hour
|
||||
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
|
||||
func updateProvisions(t *testing.T, keeper Keeper, pool Pool, ctx sdk.Context, hr int) (sdk.Rat, sdk.Int, Pool) {
|
||||
expInflation := keeper.nextInflation(ctx)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat)).EvaluateInt()
|
||||
func updateProvisions(t *testing.T, keeper Keeper, pool types.Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, types.Pool) {
|
||||
expInflation := keeper.NextInflation(ctx)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate()
|
||||
startTotalSupply := pool.TokenSupply()
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool = keeper.ProcessProvisions(ctx)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
//check provisions were added to pool
|
||||
require.Equal(t, startTotalSupply.Add(expProvisions).Int64(), pool.TokenSupply().Int64())
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
|
||||
|
||||
return expInflation, expProvisions, pool
|
||||
}
|
||||
|
||||
// Deterministic setup of validators and pool
|
||||
// Allows you to decide how many validators to setup
|
||||
// Allows you to pick which validators are bonded by adjusting the MaxValidators of params
|
||||
func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64, maxValidators uint16) ([]Validator, Keeper, Pool) {
|
||||
params := DefaultParams()
|
||||
func setupTestValidators(pool types.Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64,
|
||||
maxValidators uint16) ([]types.Validator, Keeper, types.Pool) {
|
||||
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = maxValidators
|
||||
keeper.setParams(ctx, params)
|
||||
keeper.SetParams(ctx, params)
|
||||
numValidators := len(validatorTokens)
|
||||
validators := make([]Validator, numValidators)
|
||||
validators := make([]types.Validator, numValidators)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i], pool, _ = validators[i].addTokensFromDel(pool, sdk.NewInt(validatorTokens[i]))
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[i] = keeper.updateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, validatorTokens[i])
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[i] = keeper.UpdateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
||||
pool = keeper.GetPool(ctx)
|
||||
}
|
||||
|
||||
|
@ -327,28 +331,28 @@ func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTok
|
|||
}
|
||||
|
||||
// Checks that the deterministic validator setup you wanted matches the values in the pool
|
||||
func checkValidatorSetup(t *testing.T, pool Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
||||
assert.Equal(t, initialTotalTokens, pool.TokenSupply().Int64())
|
||||
assert.Equal(t, initialBondedTokens, pool.BondedTokens.Int64())
|
||||
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens.Int64())
|
||||
func checkValidatorSetup(t *testing.T, pool types.Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
||||
assert.Equal(t, initialTotalTokens, pool.TokenSupply(), "%v", pool)
|
||||
assert.Equal(t, initialBondedTokens, pool.BondedTokens, "%v", pool)
|
||||
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens, "%v", pool)
|
||||
|
||||
// test initial bonded ratio
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.bondedRatio())
|
||||
assert.True(t, pool.BondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.BondedRatio())
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
|
||||
assert.True(t, pool.BondedShareExRate().Equal(sdk.OneRat()), "%v", pool.BondedShareExRate())
|
||||
}
|
||||
|
||||
// Checks that The inflation will correctly increase or decrease after an update to the pool
|
||||
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||
func checkInflation(t *testing.T, pool types.Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||
inflationChange := updatedInflation.Sub(previousInflation)
|
||||
|
||||
switch {
|
||||
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||
assert.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||
|
||||
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(20, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
||||
|
@ -358,11 +362,11 @@ func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation
|
|||
}
|
||||
|
||||
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||
assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||
|
||||
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(7, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// keeper of the stake store
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.Keeper
|
||||
|
||||
// codespace
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
keeper := Keeper{
|
||||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
codespace: codespace,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// return the codespace
|
||||
func (k Keeper) Codespace() sdk.CodespaceType {
|
||||
return k.codespace
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// some generic reads/writes that don't need their own files
|
||||
|
||||
// load/save the global staking params
|
||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
b := store.Get(ParamKey)
|
||||
if b == nil {
|
||||
panic("Stored params should not have been nil")
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(b, ¶ms)
|
||||
return
|
||||
}
|
||||
|
||||
// Need a distinct function because setParams depends on an existing previous
|
||||
// record of params to exist (to check if maxValidators has changed) - and we
|
||||
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
|
||||
// first params set it will panic.
|
||||
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
// set the params
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
exParams := k.GetParams(ctx)
|
||||
|
||||
// if max validator count changes, must recalculate validator set
|
||||
if exParams.MaxValidators != params.MaxValidators {
|
||||
k.UpdateBondedValidatorsFull(ctx)
|
||||
}
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the pool
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
panic("Stored pool should not have been nil")
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &pool)
|
||||
return
|
||||
}
|
||||
|
||||
// set the pool
|
||||
func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) InitIntraTxCounter(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
if b == nil {
|
||||
k.SetIntraTxCounter(ctx, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
var counter int16
|
||||
k.cdc.MustUnmarshalBinary(b, &counter)
|
||||
return counter
|
||||
}
|
||||
|
||||
// set the current in-block validator operation counter
|
||||
func (k Keeper) SetIntraTxCounter(ctx sdk.Context, counter int16) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(counter)
|
||||
store.Set(IntraTxCounterKey, bz)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
expParams := types.DefaultParams()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resParams := keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.Equal(resParams))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expParams.MaxValidators = 777
|
||||
keeper.SetParams(ctx, expParams)
|
||||
resParams = keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.Equal(resParams))
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
expPool := types.InitialPool()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.Equal(resPool))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.BondedTokens = 777
|
||||
keeper.SetPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.Equal(resPool))
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// TODO remove some of these prefixes once have working multistore
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
ParamKey = []byte{0x00} // key for parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for the staking pools
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
|
||||
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
|
||||
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
|
||||
ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator
|
||||
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
|
||||
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
|
||||
IntraTxCounterKey = []byte{0x09} // key for intra-block tx index
|
||||
DelegationKey = []byte{0x0A} // key for a delegation
|
||||
UnbondingDelegationKey = []byte{0x0B} // key for an unbonding-delegation
|
||||
UnbondingDelegationByValIndexKey = []byte{0x0C} // prefix for each key for an unbonding-delegation, by validator owner
|
||||
RedelegationKey = []byte{0x0D} // key for a redelegation
|
||||
RedelegationByValSrcIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by validator owner
|
||||
RedelegationByValDstIndexKey = []byte{0x0F} // prefix for each key for an redelegation, by validator owner
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
||||
// get the key for the validator with address
|
||||
func GetValidatorKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator with pubkey
|
||||
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
|
||||
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the current validator group, ordered like tendermint
|
||||
func GetValidatorsBondedIndexKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsBondedIndexKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the power which is the key for the validator used in the power-store
|
||||
func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte {
|
||||
|
||||
// NOTE the address doesn't need to be stored because counter bytes must always be different
|
||||
return GetValidatorPowerRank(validator, pool)
|
||||
}
|
||||
|
||||
// get the power of a validator
|
||||
func GetValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
|
||||
|
||||
power := validator.EquivalentBondedShares(pool)
|
||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
revokedBytes := make([]byte, 1)
|
||||
if validator.Revoked {
|
||||
revokedBytes[0] = byte(0x01)
|
||||
} else {
|
||||
revokedBytes[0] = byte(0x00)
|
||||
}
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
|
||||
return append(ValidatorsByPowerIndexKey,
|
||||
append(revokedBytes,
|
||||
append(powerBytes,
|
||||
append(heightBytes, counterBytes...)...)...)...)
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
|
||||
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for delegator bond with validator
|
||||
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the prefix for a delegator for all validators
|
||||
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(DelegationKey, res...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for an unbonding delegation
|
||||
func GetUBDKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetUBDsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the index-key for an unbonding delegation, stored by validator-index
|
||||
func GetUBDByValIndexKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetUBDsByValIndexKey(validatorAddr, cdc), delegatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
//______________
|
||||
|
||||
// get the prefix for all unbonding delegations from a delegator
|
||||
func GetUBDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(UnbondingDelegationKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for the indexs of unbonding delegations for a validator
|
||||
func GetUBDsByValIndexKey(validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorAddr)
|
||||
return append(UnbondingDelegationByValIndexKey, res...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for a redelegation
|
||||
func GetREDKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsKey(delegatorAddr, cdc),
|
||||
append(
|
||||
validatorSrcAddr.Bytes(),
|
||||
validatorDstAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
// get the index-key for a redelegation, stored by source-validator-index
|
||||
func GetREDByValSrcIndexKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsFromValSrcIndexKey(validatorSrcAddr, cdc),
|
||||
append(
|
||||
delegatorAddr.Bytes(),
|
||||
validatorDstAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
// get the index-key for a redelegation, stored by destination-validator-index
|
||||
func GetREDByValDstIndexKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
|
||||
append(
|
||||
delegatorAddr.Bytes(),
|
||||
validatorSrcAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
//______________
|
||||
|
||||
// get the prefix keyspace for redelegations from a delegator
|
||||
func GetREDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(RedelegationKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating away from a source validator
|
||||
func GetREDsFromValSrcIndexKey(validatorSrcAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorSrcAddr)
|
||||
return append(RedelegationByValSrcIndexKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating towards a destination validator
|
||||
func GetREDsToValDstIndexKey(validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorDstAddr)
|
||||
return append(RedelegationByValDstIndexKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating towards a destination validator
|
||||
// from a particular delegator
|
||||
func GetREDsByDelToValDstIndexKey(delegatorAddr sdk.Address,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
|
||||
delegatorAddr.Bytes()...)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// Implements ValidatorSet
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// get the sdk.validator for a particular address
|
||||
func (k Keeper) Validator(ctx sdk.Context, address sdk.Address) sdk.Validator {
|
||||
val, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// total power from the bond
|
||||
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
||||
pool := k.GetPool(ctx)
|
||||
return pool.BondedShares
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements DelegationSet
|
||||
|
||||
var _ sdk.DelegationSet = Keeper{}
|
||||
|
||||
// Returns self as it is both a validatorset and delegationset
|
||||
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
|
||||
return k
|
||||
}
|
||||
|
||||
// get the delegation for a particular set of delegator and validator addresses
|
||||
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
|
||||
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := GetDelegationsKey(delAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, key)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bz, &delegation)
|
||||
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// NOTE the current slash functionality doesn't take into consideration unbonding/rebonding records
|
||||
// or the time of breach. This will be updated in slashing v2
|
||||
// slash a validator
|
||||
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
|
||||
|
||||
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Attempted to slash a nonexistent validator with address %s", pubkey.Address()))
|
||||
}
|
||||
sharesToRemove := validator.PoolShares.Amount.Mul(fraction)
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, burned := validator.RemovePoolShares(pool, sharesToRemove)
|
||||
k.SetPool(ctx, pool) // update the pool
|
||||
k.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %d tokens", pubkey.Address(), fraction, sharesToRemove, burned))
|
||||
return
|
||||
}
|
||||
|
||||
// revoke a validator
|
||||
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot revoke", pubkey))
|
||||
}
|
||||
validator.Revoked = true
|
||||
k.UpdateValidator(ctx, validator) // update the validator, now revoked
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// unrevoke a validator
|
||||
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot unrevoke", pubkey))
|
||||
}
|
||||
validator.Revoked = false
|
||||
k.UpdateValidator(ctx, validator) // update the validator, now unrevoked
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -18,35 +18,52 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// dummy addresses used for testing
|
||||
var (
|
||||
addrs = createTestAddrs(100)
|
||||
pks = createTestPubKeys(100)
|
||||
Addrs = createTestAddrs(100)
|
||||
PKs = createTestPubKeys(100)
|
||||
emptyAddr sdk.Address
|
||||
emptyPubkey crypto.PubKey
|
||||
|
||||
addrDels = []sdk.Address{
|
||||
Addrs[0],
|
||||
Addrs[1],
|
||||
}
|
||||
addrVals = []sdk.Address{
|
||||
Addrs[2],
|
||||
Addrs[3],
|
||||
Addrs[4],
|
||||
Addrs[5],
|
||||
Addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
// intended to be used with require/assert: require.True(ValEq(...))
|
||||
func ValEq(t *testing.T, exp, got Validator) (*testing.T, bool, string, Validator, Validator) {
|
||||
return t, exp.equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) {
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
func makeTestCodec() *wire.Codec {
|
||||
// create a codec used only for testing
|
||||
func MakeTestCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil)
|
||||
cdc.RegisterConcrete(MsgCreateValidator{}, "test/stake/CreateValidator", nil)
|
||||
cdc.RegisterConcrete(MsgEditValidator{}, "test/stake/EditValidator", nil)
|
||||
cdc.RegisterConcrete(MsgUnbond{}, "test/stake/Unbond", nil)
|
||||
cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil)
|
||||
cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil)
|
||||
cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil)
|
||||
cdc.RegisterConcrete(types.MsgCompleteUnbonding{}, "test/stake/CompleteUnbonding", nil)
|
||||
cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/stake/BeginRedelegate", nil)
|
||||
cdc.RegisterConcrete(types.MsgCompleteRedelegate{}, "test/stake/CompleteRedelegate", nil)
|
||||
|
||||
// Register AppAccount
|
||||
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
||||
|
@ -56,8 +73,9 @@ func makeTestCodec() *wire.Codec {
|
|||
return cdc
|
||||
}
|
||||
|
||||
func paramsNoInflation() Params {
|
||||
return Params{
|
||||
// default params without inflation
|
||||
func ParamsNoInflation() types.Params {
|
||||
return types.Params{
|
||||
InflationRateChange: sdk.ZeroRat(),
|
||||
InflationMax: sdk.ZeroRat(),
|
||||
InflationMin: sdk.ZeroRat(),
|
||||
|
@ -68,7 +86,7 @@ func paramsNoInflation() Params {
|
|||
}
|
||||
|
||||
// hogpodge of all sorts of input required for testing
|
||||
func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Context, auth.AccountMapper, Keeper) {
|
||||
func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) {
|
||||
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
keyAcc := sdk.NewKVStoreKey("acc")
|
||||
|
@ -81,28 +99,32 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Conte
|
|||
require.Nil(t, err)
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
|
||||
cdc := makeTestCodec()
|
||||
cdc := MakeTestCodec()
|
||||
accountMapper := auth.NewAccountMapper(
|
||||
cdc, // amino codec
|
||||
keyAcc, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
ck := bank.NewKeeper(accountMapper)
|
||||
keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace)
|
||||
keeper.setPool(ctx, InitialPool())
|
||||
keeper.setNewParams(ctx, DefaultParams())
|
||||
keeper := NewKeeper(cdc, keyStake, ck, types.DefaultCodespace)
|
||||
keeper.SetPool(ctx, types.InitialPool())
|
||||
keeper.SetNewParams(ctx, types.DefaultParams())
|
||||
keeper.InitIntraTxCounter(ctx)
|
||||
|
||||
// fill all the addresses with some coins
|
||||
for _, addr := range addrs {
|
||||
// fill all the addresses with some coins, set the loose pool tokens simultaneously
|
||||
for _, addr := range Addrs {
|
||||
pool := keeper.GetPool(ctx)
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
{keeper.GetParams(ctx).BondDenom, initCoins},
|
||||
{keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
|
||||
})
|
||||
pool.LooseTokens += initCoins
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
return ctx, accountMapper, keeper
|
||||
}
|
||||
|
||||
func newPubKey(pk string) (res crypto.PubKey) {
|
||||
func NewPubKey(pk string) (res crypto.PubKey) {
|
||||
pkBytes, err := hex.DecodeString(pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -114,7 +136,7 @@ func newPubKey(pk string) (res crypto.PubKey) {
|
|||
}
|
||||
|
||||
// for incode address generation
|
||||
func testAddr(addr string, bech string) sdk.Address {
|
||||
func TestAddr(addr string, bech string) sdk.Address {
|
||||
|
||||
res, err := sdk.GetAccAddressHex(addr)
|
||||
if err != nil {
|
||||
|
@ -151,7 +173,7 @@ func createTestAddrs(numAddrs int) []sdk.Address {
|
|||
buffer.WriteString(numString) //adding on final two digits to make addresses unique
|
||||
res, _ := sdk.GetAccAddressHex(buffer.String())
|
||||
bech, _ := sdk.Bech32ifyAcc(res)
|
||||
addresses = append(addresses, testAddr(buffer.String(), bech))
|
||||
addresses = append(addresses, TestAddr(buffer.String(), bech))
|
||||
buffer.Reset()
|
||||
}
|
||||
return addresses
|
||||
|
@ -166,8 +188,16 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
|
|||
numString := strconv.Itoa(i)
|
||||
buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string
|
||||
buffer.WriteString(numString) //adding on final two digits to make pubkeys unique
|
||||
publicKeys = append(publicKeys, newPubKey(buffer.String()))
|
||||
publicKeys = append(publicKeys, NewPubKey(buffer.String()))
|
||||
buffer.Reset()
|
||||
}
|
||||
return publicKeys
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// does a certain by-power index record exist
|
||||
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
|
@ -0,0 +1,537 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// get a single validator
|
||||
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator types.Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(GetValidatorKey(addr))
|
||||
if b == nil {
|
||||
return validator, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &validator)
|
||||
return validator, true
|
||||
}
|
||||
|
||||
// get a single validator by pubkey
|
||||
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator types.Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
|
||||
if addr == nil {
|
||||
return validator, false
|
||||
}
|
||||
return k.GetValidator(ctx, addr)
|
||||
}
|
||||
|
||||
// set the main record holding validator details
|
||||
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set main store
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bz)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorByPubKeyIndex(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set pointer by pubkey
|
||||
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.Owner)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
|
||||
}
|
||||
|
||||
// used in testing
|
||||
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
||||
|
||||
// Get the set of all validators with no limits, used during genesis dump
|
||||
func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators = append(validators, validator)
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators
|
||||
}
|
||||
|
||||
// Get the set of all validators, retrieve a maxRetrieve number of records
|
||||
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
validators = make([]types.Validator, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators[i] = validator
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// get the group of the bonded validators
|
||||
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators = make([]types.Validator, maxValidators)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
i := 0
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
// sanity check
|
||||
if i > int(maxValidators-1) {
|
||||
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// get the group of bonded validators sorted by power-rank
|
||||
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators := make([]types.Validator, maxValidators)
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
i := 0
|
||||
for {
|
||||
if !iterator.Valid() || i > int(maxValidators-1) {
|
||||
break
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
if validator.Status() == sdk.Bonded {
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// Accumulated updates to the active/bonded validator set for tendermint
|
||||
|
||||
// get the most recently updated validators
|
||||
func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val abci.Validator
|
||||
k.cdc.MustUnmarshalBinary(valBytes, &val)
|
||||
updates = append(updates, val)
|
||||
}
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries after applied to Tendermint
|
||||
func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// delete subspace
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// perfom all the nessisary steps for when a validator changes its power
|
||||
// updates all validator stores as well as tendermint update store
|
||||
// may kick out validators if new validator is entering the bonded validator group
|
||||
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
ownerAddr := validator.Owner
|
||||
|
||||
// always update the main list ordered by owner address before exiting
|
||||
defer func() {
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(ownerAddr), bz)
|
||||
}()
|
||||
|
||||
// retrieve the old validator record
|
||||
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
|
||||
|
||||
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
validator = k.unbondValidator(ctx, validator)
|
||||
|
||||
// need to also clear the cliff validator spot because the revoke has
|
||||
// opened up a new spot which will be filled when
|
||||
// updateValidatorsBonded is called
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
|
||||
powerIncreasing := false
|
||||
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
|
||||
powerIncreasing = true
|
||||
}
|
||||
|
||||
// if already a validator, copy the old block height and counter, else set them
|
||||
if oldFound && oldValidator.Status() == sdk.Bonded {
|
||||
validator.BondHeight = oldValidator.BondHeight
|
||||
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
|
||||
} else {
|
||||
validator.BondHeight = ctx.BlockHeight()
|
||||
counter := k.GetIntraTxCounter(ctx)
|
||||
validator.BondIntraTxCounter = counter
|
||||
k.SetIntraTxCounter(ctx, counter+1)
|
||||
}
|
||||
|
||||
// update the list ordered by voting power
|
||||
if oldFound {
|
||||
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
|
||||
}
|
||||
valPower := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
store.Set(valPower, validator.Owner)
|
||||
|
||||
// efficiency case:
|
||||
// if already bonded and power increasing only need to update tendermint
|
||||
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
|
||||
return validator
|
||||
}
|
||||
|
||||
// efficiency case:
|
||||
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
|
||||
cliffPower := k.getCliffValidatorPower(ctx)
|
||||
if cliffPower != nil &&
|
||||
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
|
||||
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
|
||||
return validator
|
||||
}
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal := k.UpdateBondedValidators(ctx, validator)
|
||||
if updatedVal.Owner != nil { // updates to validator occurred to be updated
|
||||
validator = updatedVal
|
||||
}
|
||||
return validator
|
||||
}
|
||||
|
||||
// Update the validator group and kick out any old validators. In addition this
|
||||
// function adds (or doesn't add) a validator which has updated its bonded
|
||||
// tokens to the validator group. -> this validator is specified through the
|
||||
// updatedValidatorAddr term.
|
||||
//
|
||||
// The correct subset is retrieved by iterating through an index of the
|
||||
// validators sorted by power, stored using the ValidatorsByPowerIndexKey.
|
||||
// Simultaneously the current validator records are updated in store with the
|
||||
// ValidatorsBondedIndexKey. This store is used to determine if a validator is a
|
||||
// validator without needing to iterate over the subspace as we do in
|
||||
// GetValidators.
|
||||
//
|
||||
// Optionally also return the validator from a retrieve address if the validator has been bonded
|
||||
func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
|
||||
newValidator types.Validator) (updatedVal types.Validator) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
kickCliffValidator := false
|
||||
oldCliffValidatorAddr := k.getCliffValidator(ctx)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator types.Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
// TODO benchmark if we should read the current power and not write if it's the same
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store, or under the
|
||||
// situation that this is the "new validator" just use the validator
|
||||
// provided because it has not yet been updated in the main validator
|
||||
// store
|
||||
ownerAddr := iterator.Value()
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
validator = newValidator
|
||||
} else {
|
||||
var found bool
|
||||
validator, found = k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
}
|
||||
|
||||
// if not previously a validator (and unrevoked),
|
||||
// kick the cliff validator / bond this new validator
|
||||
if validator.Status() != sdk.Bonded && !validator.Revoked {
|
||||
kickCliffValidator = true
|
||||
|
||||
validator = k.bondValidator(ctx, validator)
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
updatedVal = validator
|
||||
}
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// perform the actual kicks
|
||||
if oldCliffValidatorAddr != nil && kickCliffValidator {
|
||||
validator, found := k.GetValidator(ctx, oldCliffValidatorAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// full update of the bonded validator set, many can be added/kicked
|
||||
func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// clear the current validators store, add to the ToKickOut temp store
|
||||
toKickOut := make(map[string]byte)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
ownerAddr := iterator.Value()
|
||||
toKickOut[string(ownerAddr)] = 0 // set anything
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator types.Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store,
|
||||
// or under the situation that this is the "new validator" just
|
||||
// use the validator provided because it has not yet been updated
|
||||
// in the main validator store
|
||||
ownerAddr := iterator.Value()
|
||||
var found bool
|
||||
validator, found = k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
|
||||
_, found = toKickOut[string(ownerAddr)]
|
||||
if found {
|
||||
delete(toKickOut, string(ownerAddr))
|
||||
} else {
|
||||
|
||||
// if it wasn't in the toKickOut group it means
|
||||
// this wasn't a previously a validator, therefor
|
||||
// update the validator to enter the validator group
|
||||
validator = k.bondValidator(ctx, validator)
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// perform the actual kicks
|
||||
for key := range toKickOut {
|
||||
ownerAddr := []byte(key)
|
||||
validator, found := k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes unbonded
|
||||
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Unbonded {
|
||||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// save the now unbonded validator record
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
// also remove from the Bonded types.Validators Store
|
||||
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
|
||||
return validator
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes bonded
|
||||
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// save the now bonded validator record to the three referenced stores
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
// remove the validator record and associated indexes
|
||||
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.Address) {
|
||||
|
||||
// first retrieve the old validator record
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
// delete the old validator record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
store.Delete(GetValidatorKey(address))
|
||||
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
|
||||
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
|
||||
|
||||
// delete from the current and power weighted validator groups if the validator
|
||||
// is bonded - and add validator with zero power to the validator updates
|
||||
if store.Get(GetValidatorsBondedIndexKey(validator.Owner)) == nil {
|
||||
return
|
||||
}
|
||||
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
|
||||
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(address), bz)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current validator on the cliff
|
||||
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorCliffIndexKey)
|
||||
}
|
||||
|
||||
// get the current power of the validator on the cliff
|
||||
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorPowerCliffKey)
|
||||
}
|
||||
|
||||
// set the current validator and power of the validator on the cliff
|
||||
func (k Keeper) setCliffValidator(ctx sdk.Context, validator types.Validator, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
store.Set(ValidatorPowerCliffKey, bz)
|
||||
store.Set(ValidatorCliffIndexKey, validator.Owner)
|
||||
}
|
||||
|
||||
// clear the current validator and power of the validator on the cliff
|
||||
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(ValidatorPowerCliffKey)
|
||||
store.Delete(ValidatorCliffIndexKey)
|
||||
}
|
|
@ -0,0 +1,674 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 10)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// test how the validator is set from a purely unbonbed pool
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, _ = validator.AddTokensFromDel(pool, 10)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validator)
|
||||
|
||||
// after the save the validator should be bonded
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
|
||||
// Check each store for being saved
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.True(ValEq(t, validator, resVal))
|
||||
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
resVals = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validator.ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create a random pool
|
||||
pool.LooseTokens = 10000
|
||||
pool.BondedTokens = 1234
|
||||
pool.BondedShares = sdk.NewRat(124)
|
||||
pool.UnbondingTokens = 13934
|
||||
pool.UnbondingShares = sdk.NewRat(145)
|
||||
pool.UnbondedTokens = 154
|
||||
pool.UnbondedShares = sdk.NewRat(1333)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// add a validator
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, delSharesCreated := validator.AddTokensFromDel(pool, 100)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validator)
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// burn half the delegator shares
|
||||
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||
assert.Equal(t, int64(50), burned)
|
||||
keeper.SetPool(ctx, pool) // update the pool
|
||||
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
power = GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
}
|
||||
|
||||
// This function tests UpdateValidator, GetValidator, GetValidatorsBonded, RemoveValidator
|
||||
func TestValidatorBasics(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
var validators [3]types.Validator
|
||||
amts := []int64{9, 8, 7}
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||
validators[i].PoolShares = types.NewUnbondedShares(sdk.ZeroRat())
|
||||
validators[i].AddTokensFromDel(pool, amt)
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
assert.Zero(t, len(resVals))
|
||||
|
||||
// set and retrieve a record
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(10))
|
||||
validators[0].DelegatorShares = sdk.NewRat(10)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// add other validators
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[1], resVal))
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[2])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[2], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 3, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here
|
||||
assert.True(ValEq(t, validators[1], resVals[1]))
|
||||
assert.True(ValEq(t, validators[2], resVals[2]))
|
||||
|
||||
// remove a record
|
||||
keeper.RemoveValidator(ctx, validators[1].Owner)
|
||||
_, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetValidatorsByPower
|
||||
func GetValidatorSortingUnmixed(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i].PoolShares = types.NewBondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
|
||||
// test a basic increase in voting power
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(500))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(200))
|
||||
ctx = ctx.WithBlockHeight(10)
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
|
||||
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
|
||||
|
||||
// no change in voting power - no change in sort
|
||||
ctx = ctx.WithBlockHeight(20)
|
||||
keeper.UpdateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
ctx = ctx.WithBlockHeight(30)
|
||||
keeper.UpdateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n, "%v", resValidators)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
}
|
||||
|
||||
func GetValidatorSortingMixed(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
|
||||
n := len(amts)
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[0]))
|
||||
validators[1].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[1]))
|
||||
validators[2].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[2]))
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(amts[3]))
|
||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(amts[4]))
|
||||
for i := range amts {
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
val0, found := keeper.GetValidator(ctx, Addrs[0])
|
||||
require.True(t, found)
|
||||
val1, found := keeper.GetValidator(ctx, Addrs[1])
|
||||
require.True(t, found)
|
||||
val2, found := keeper.GetValidator(ctx, Addrs[2])
|
||||
require.True(t, found)
|
||||
val3, found := keeper.GetValidator(ctx, Addrs[3])
|
||||
require.True(t, found)
|
||||
val4, found := keeper.GetValidator(ctx, Addrs[4])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, sdk.Unbonded, val0.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val1.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val2.Status())
|
||||
assert.Equal(t, sdk.Bonded, val3.Status())
|
||||
assert.Equal(t, sdk.Bonded, val4.Status())
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
}
|
||||
|
||||
// TODO separate out into multiple tests
|
||||
func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
var found bool
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
nMax := uint16(2)
|
||||
params.MaxValidators = nMax
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400}
|
||||
var validators [4]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[i] = keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 500)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// A validator which leaves the gotValidator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
|
||||
// validator 3 enters bonded validator set
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
|
||||
validators[3], found = keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.True(t, found)
|
||||
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 1)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// validator 3 kicked out temporarily
|
||||
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewRat(201))
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// validator 4 does not get spot back
|
||||
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 200)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
validator, exists := keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.Equal(t, exists, true)
|
||||
require.Equal(t, int64(40), validator.BondHeight)
|
||||
}
|
||||
|
||||
func TestValidatorBondHeight(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
pool := keeper.GetPool(ctx)
|
||||
var validators [3]types.Validator
|
||||
validators[0] = types.NewValidator(Addrs[0], PKs[0], types.Description{})
|
||||
validators[1] = types.NewValidator(Addrs[1], PKs[1], types.Description{})
|
||||
validators[2] = types.NewValidator(Addrs[2], PKs[2], types.Description{})
|
||||
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 200)
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 100)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 100)
|
||||
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
////////////////////////////////////////
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should become bonded
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
|
||||
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[1], resValidators[1]))
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 50)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 50)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestFullValidatorSetPowerChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
params := keeper.GetParams(ctx)
|
||||
max := 2
|
||||
params.MaxValidators = uint16(2)
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400, 200}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
var found bool
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
assert.Equal(t, sdk.Unbonded, validators[0].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[1].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[2].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[3].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[4].Status())
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// test a swap in voting power
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 600)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
// clear the tracked changes to the gotValidator set
|
||||
func TestClearTendermintUpdates(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{100, 400, 200}
|
||||
validators := make([]types.Validator, len(amts))
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
assert.Equal(t, len(amts), len(updates))
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(updates))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesAllNone(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
// test from nothing to something
|
||||
// tendermintUpdate set: {} -> {c1, c3}
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
assert.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
|
||||
|
||||
// test from something to nothing
|
||||
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
keeper.RemoveValidator(ctx, validators[0].Owner)
|
||||
keeper.RemoveValidator(ctx, validators[1].Owner)
|
||||
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
|
||||
assert.Equal(t, int64(0), updates[0].Power)
|
||||
assert.Equal(t, int64(0), updates[1].Power)
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesIdentical(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test identical,
|
||||
// tendermintUpdate set: {} -> {}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test single value change
|
||||
// tendermintUpdate set: {} -> {c1'}
|
||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(600))
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test multiple value change
|
||||
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 190)
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 80)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
require.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesInserted(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20, 5, 15, 25}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[3].ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the end
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
validators[4] = keeper.UpdateValidator(ctx, validators[4])
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[4].ABCIValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
amts := []int64{10, 20, 5}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test validator added at the end but not inserted in the valset
|
||||
// tendermintUpdate set: {} -> {}
|
||||
keeper.UpdateValidator(ctx, validators[2])
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 0, len(updates))
|
||||
|
||||
// test validator change its power and become a gotValidator (pushing out an existing)
|
||||
// tendermintUpdate set: {} -> {c0, c4}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 10)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates), "%v", updates)
|
||||
require.Equal(t, validators[0].ABCIValidatorZero(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[1])
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// TODO remove some of these prefixes once have working multistore
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
ParamKey = []byte{0x00} // key for parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for the staking pools
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator by pubkey
|
||||
ValidatorsBondedKey = []byte{0x04} // prefix for each key to bonded/actively validating validators
|
||||
ValidatorsByPowerKey = []byte{0x05} // prefix for each key to a validator sorted by power
|
||||
ValidatorCliffKey = []byte{0x06} // key for block-local tx index
|
||||
ValidatorPowerCliffKey = []byte{0x07} // key for block-local tx index
|
||||
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
|
||||
DelegationKey = []byte{0x09} // prefix for each key to a delegator's bond
|
||||
IntraTxCounterKey = []byte{0x10} // key for block-local tx index
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
||||
// get the key for the validator with address
|
||||
func GetValidatorKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator with pubkey
|
||||
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
|
||||
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the current validator group, ordered like tendermint
|
||||
func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
|
||||
addr := pk.Address()
|
||||
return append(ValidatorsBondedKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator used in the power-store
|
||||
func GetValidatorsByPowerKey(validator Validator, pool Pool) []byte {
|
||||
|
||||
power := validator.EquivalentBondedShares(pool)
|
||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
revokedBytes := make([]byte, 1)
|
||||
if validator.Revoked {
|
||||
revokedBytes[0] = byte(0x01)
|
||||
} else {
|
||||
revokedBytes[0] = byte(0x00)
|
||||
}
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
return append(ValidatorsByPowerKey,
|
||||
append(revokedBytes,
|
||||
append(powerBytes,
|
||||
append(heightBytes,
|
||||
append(counterBytes, validator.Owner.Bytes()...)...)...)...)...) // TODO don't technically need to store owner
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
|
||||
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for delegator bond with validator
|
||||
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the prefix for a delegator for all validators
|
||||
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalBinary(&delegatorAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegationKey, res...)
|
||||
}
|
|
@ -1,792 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
addrDels = []sdk.Address{
|
||||
addrs[0],
|
||||
addrs[1],
|
||||
}
|
||||
addrVals = []sdk.Address{
|
||||
addrs[2],
|
||||
addrs[3],
|
||||
addrs[4],
|
||||
addrs[5],
|
||||
addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create a random pool
|
||||
pool.BondedTokens = sdk.NewInt(1234)
|
||||
pool.BondedShares = sdk.NewRat(124)
|
||||
pool.UnbondingTokens = sdk.NewInt(13934)
|
||||
pool.UnbondingShares = sdk.NewRat(145)
|
||||
pool.UnbondedTokens = sdk.NewInt(154)
|
||||
pool.UnbondedShares = sdk.NewRat(1333)
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// add a validator
|
||||
validator := NewValidator(addrVals[0], pks[0], Description{})
|
||||
validator, pool, delSharesCreated := validator.addTokensFromDel(pool, sdk.NewInt(100))
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.updateValidator(ctx, validator)
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// burn half the delegator shares
|
||||
validator, pool, burned := validator.removeDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||
assert.Equal(t, int64(50), burned.Int64())
|
||||
keeper.setPool(ctx, pool) // update the pool
|
||||
keeper.updateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
power = GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
}
|
||||
|
||||
func TestSetValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// test how the validator is set from a purely unbonbed pool
|
||||
validator := NewValidator(addrVals[0], pks[0], Description{})
|
||||
validator, pool, _ = validator.addTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.updateValidator(ctx, validator)
|
||||
|
||||
// after the save the validator should be bonded
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
|
||||
// Check each store for being saved
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.True(ValEq(t, validator, resVal))
|
||||
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
resVals = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validator.abciValidator(keeper.cdc), updates[0])
|
||||
|
||||
}
|
||||
|
||||
// This function tests updateValidator, GetValidator, GetValidatorsBonded, removeValidator
|
||||
func TestValidatorBasics(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
var validators [3]Validator
|
||||
amts := []int64{9, 8, 7}
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrVals[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.ZeroRat())
|
||||
validators[i].addTokensFromDel(pool, sdk.NewInt(amt))
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
assert.Zero(t, len(resVals))
|
||||
|
||||
// set and retrieve a record
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
validators[0].PoolShares = NewBondedShares(sdk.NewRat(10))
|
||||
validators[0].DelegatorShares = sdk.NewRat(10)
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// add other validators
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[1], resVal))
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[2])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[2], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 3, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[2])) // order doesn't matter here
|
||||
assert.True(ValEq(t, validators[1], resVals[1]))
|
||||
assert.True(ValEq(t, validators[2], resVals[0]))
|
||||
|
||||
// remove a record
|
||||
keeper.removeValidator(ctx, validators[1].Owner)
|
||||
_, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetValidatorsByPower
|
||||
func GetValidatorSortingUnmixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewBondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
|
||||
// test a basic increase in voting power
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(500))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(200))
|
||||
ctx = ctx.WithBlockHeight(10)
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
|
||||
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
|
||||
|
||||
// no change in voting power - no change in sort
|
||||
ctx = ctx.WithBlockHeight(20)
|
||||
keeper.updateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
validators[4].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
ctx = ctx.WithBlockHeight(30)
|
||||
keeper.updateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n, "%v", resValidators)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
}
|
||||
|
||||
func GetValidatorSortingMixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
|
||||
n := len(amts)
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(amts[0]))
|
||||
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(amts[1]))
|
||||
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(amts[2]))
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(amts[3]))
|
||||
validators[4].PoolShares = NewBondedShares(sdk.NewRat(amts[4]))
|
||||
for i := range amts {
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
val0, found := keeper.GetValidator(ctx, addrs[0])
|
||||
require.True(t, found)
|
||||
val1, found := keeper.GetValidator(ctx, addrs[1])
|
||||
require.True(t, found)
|
||||
val2, found := keeper.GetValidator(ctx, addrs[2])
|
||||
require.True(t, found)
|
||||
val3, found := keeper.GetValidator(ctx, addrs[3])
|
||||
require.True(t, found)
|
||||
val4, found := keeper.GetValidator(ctx, addrs[4])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, sdk.Unbonded, val0.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val1.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val2.Status())
|
||||
assert.Equal(t, sdk.Bonded, val3.Status())
|
||||
assert.Equal(t, sdk.Bonded, val4.Status())
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
}
|
||||
|
||||
// TODO separate out into multiple tests
|
||||
func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
var found bool
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
nMax := uint16(2)
|
||||
params.MaxValidators = nMax
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400}
|
||||
var validators [4]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
validators[i] = keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(500))
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// A validator which leaves the gotValidator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
|
||||
// validator 3 enters bonded validator set
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
|
||||
validators[3], found = keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.True(t, found)
|
||||
validators[3].PoolShares = NewUnbondedShares(sdk.NewRat(401))
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// validator 3 kicked out temporarily
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(200))
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// validator 4 does not get spot back
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(400))
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
validator, exists := keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.Equal(t, exists, true)
|
||||
require.Equal(t, int64(40), validator.BondHeight)
|
||||
}
|
||||
|
||||
func TestValidatorBondHeight(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
var validators [3]Validator
|
||||
validators[0] = NewValidator(addrs[0], pks[0], Description{})
|
||||
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(200))
|
||||
validators[0].DelegatorShares = sdk.NewRat(200)
|
||||
validators[1] = NewValidator(addrs[1], pks[1], Description{})
|
||||
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(100))
|
||||
validators[1].DelegatorShares = sdk.NewRat(100)
|
||||
validators[2] = NewValidator(addrs[2], pks[2], Description{})
|
||||
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(100))
|
||||
validators[2].DelegatorShares = sdk.NewRat(100)
|
||||
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
////////////////////////////////////////
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should become bonded
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
|
||||
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[1], resValidators[1]))
|
||||
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(150))
|
||||
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(150))
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestFullValidatorSetPowerChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
params := keeper.GetParams(ctx)
|
||||
max := 2
|
||||
params.MaxValidators = uint16(2)
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400, 200}
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
var found bool
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
assert.Equal(t, sdk.Unbonded, validators[0].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[1].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[2].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[3].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[4].Status())
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// test a swap in voting power
|
||||
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(600))
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
// clear the tracked changes to the gotValidator set
|
||||
func TestClearTendermintUpdates(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{100, 400, 200}
|
||||
validators := make([]Validator, len(amts))
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
assert.Equal(t, len(amts), len(updates))
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
updates = keeper.getTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(updates))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesAllNone(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
|
||||
// test from nothing to something
|
||||
// tendermintUpdate set: {} -> {c1, c3}
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
|
||||
assert.Equal(t, validators[1].abciValidator(keeper.cdc), updates[1])
|
||||
|
||||
// test from something to nothing
|
||||
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
keeper.removeValidator(ctx, validators[0].Owner)
|
||||
keeper.removeValidator(ctx, validators[1].Owner)
|
||||
|
||||
updates = keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
|
||||
assert.Equal(t, int64(0), updates[0].Power)
|
||||
assert.Equal(t, int64(0), updates[1].Power)
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesIdentical(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
// test identical,
|
||||
// tendermintUpdate set: {} -> {}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
// test single value change
|
||||
// tendermintUpdate set: {} -> {c1'}
|
||||
validators[0].PoolShares = NewBondedShares(sdk.NewRat(600))
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
// test multiple value change
|
||||
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
|
||||
validators[0].PoolShares = NewBondedShares(sdk.NewRat(200))
|
||||
validators[1].PoolShares = NewBondedShares(sdk.NewRat(100))
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
require.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[1].abciValidator(keeper.cdc), updates[1])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesInserted(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
amts := []int64{10, 20, 5, 15, 25}
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[2].abciValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
updates = keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[3].abciValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the end
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
validators[4] = keeper.updateValidator(ctx, validators[4])
|
||||
updates = keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[4].abciValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
params := DefaultParams()
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
amts := []int64{10, 20, 5}
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
// test validator added at the end but not inserted in the valset
|
||||
// tendermintUpdate set: {} -> {}
|
||||
keeper.updateValidator(ctx, validators[2])
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 0, len(updates))
|
||||
|
||||
// test validator change its power and become a gotValidator (pushing out an existing)
|
||||
// tendermintUpdate set: {} -> {c0, c4}
|
||||
keeper.clearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
|
||||
|
||||
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(15))
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
|
||||
updates = keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates), "%v", updates)
|
||||
require.Equal(t, validators[0].abciValidatorZero(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[2].abciValidator(keeper.cdc), updates[1])
|
||||
}
|
||||
|
||||
// tests GetDelegation, GetDelegations, SetDelegation, removeDelegation, GetBonds
|
||||
func TestBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
//construct the validators
|
||||
amts := []int64{9, 8, 7}
|
||||
var validators [3]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrVals[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
|
||||
// first add a validators[0] to delegate too
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
|
||||
bond1to1 := Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// add some more records
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegation(ctx, bond1to2)
|
||||
keeper.setDelegation(ctx, bond1to3)
|
||||
keeper.setDelegation(ctx, bond2to1)
|
||||
keeper.setDelegation(ctx, bond2to2)
|
||||
keeper.setDelegation(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond1to1.equal(resBonds[0]))
|
||||
assert.True(t, bond1to2.equal(resBonds[1]))
|
||||
assert.True(t, bond1to3.equal(resBonds[2]))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond2to1.equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.equal(resBonds[1]))
|
||||
assert.True(t, bond2to3.equal(resBonds[2]))
|
||||
allBonds := keeper.getAllDelegations(ctx)
|
||||
require.Equal(t, 6, len(allBonds))
|
||||
assert.True(t, bond1to1.equal(allBonds[0]))
|
||||
assert.True(t, bond1to2.equal(allBonds[1]))
|
||||
assert.True(t, bond1to3.equal(allBonds[2]))
|
||||
assert.True(t, bond2to1.equal(allBonds[3]))
|
||||
assert.True(t, bond2to2.equal(allBonds[4]))
|
||||
assert.True(t, bond2to3.equal(allBonds[5]))
|
||||
|
||||
// delete a record
|
||||
keeper.removeDelegation(ctx, bond2to3)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
assert.True(t, bond2to1.equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.equal(resBonds[1]))
|
||||
|
||||
// delete all the records from delegator 2
|
||||
keeper.removeDelegation(ctx, bond2to1)
|
||||
keeper.removeDelegation(ctx, bond2to2)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
|
||||
assert.False(t, found)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 0, len(resBonds))
|
||||
}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
expParams := DefaultParams()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resParams := keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.equal(resParams))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expParams.MaxValidators = 777
|
||||
keeper.setParams(ctx, expParams)
|
||||
resParams = keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.equal(resParams))
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
expPool := InitialPool()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.equal(resPool))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.BondedTokens = sdk.NewInt(777)
|
||||
keeper.setPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.equal(resPool))
|
||||
}
|
243
x/stake/msg.go
243
x/stake/msg.go
|
@ -1,243 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// name to idetify transaction types
|
||||
const MsgType = "stake"
|
||||
|
||||
// XXX remove: think it makes more sense belonging with the Params so we can
|
||||
// initialize at genesis - to allow for the same tests we should should make
|
||||
// the ValidateBasic() function a return from an initializable function
|
||||
// ValidateBasic(bondDenom string) function
|
||||
const StakingToken = "steak"
|
||||
|
||||
//Verify interface at compile time
|
||||
var _, _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}, &MsgUnbond{}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgCreateValidator - struct for unbonding transactions
|
||||
type MsgCreateValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgCreateValidator(validatorAddr sdk.Address, pubkey crypto.PubKey,
|
||||
bond sdk.Coin, description Description) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
PubKey: pubkey,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgCreateValidator) Type() string { return MsgType }
|
||||
func (msg MsgCreateValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCreateValidator) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
PubKey: sdk.MustBech32ifyValPub(msg.PubKey),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Amount.Sign() != 1 {
|
||||
return ErrBadBondingAmount(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(DefaultCodespace, CodeInvalidInput, "description must be included")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgEditValidator - struct for editing a validator
|
||||
type MsgEditValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
}
|
||||
|
||||
func NewMsgEditValidator(validatorAddr sdk.Address, description Description) MsgEditValidator {
|
||||
return MsgEditValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgEditValidator) Type() string { return MsgType }
|
||||
func (msg MsgEditValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgEditValidator) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgEditValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgDelegate) Type() string { return MsgType }
|
||||
func (msg MsgDelegate) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.DelegatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgDelegate) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Bond: msg.Bond,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgDelegate) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Amount.Sign() != 1 {
|
||||
return ErrBadBondingAmount(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgUnbond - struct for unbonding transactions
|
||||
type MsgUnbond struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
func NewMsgUnbond(delegatorAddr, validatorAddr sdk.Address, shares string) MsgUnbond {
|
||||
return MsgUnbond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: shares,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgUnbond) Type() string { return MsgType }
|
||||
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgUnbond) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Shares string `json:"shares"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Shares: msg.Shares,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgUnbond) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Shares != "MAX" {
|
||||
rat, err := sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return ErrBadShares(DefaultCodespace)
|
||||
}
|
||||
if rat.IsZero() || rat.LT(sdk.ZeroRat()) {
|
||||
return ErrBadShares(DefaultCodespace)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
coinPos = sdk.NewCoin("steak", 1000)
|
||||
coinZero = sdk.NewCoin("steak", 0)
|
||||
coinNeg = sdk.NewCoin("steak", -10000)
|
||||
coinPosNotAtoms = sdk.NewCoin("foo", 10000)
|
||||
coinZeroNotAtoms = sdk.NewCoin("foo", 0)
|
||||
coinNegNotAtoms = sdk.NewCoin("foo", -10000)
|
||||
)
|
||||
|
||||
// test ValidateBasic for MsgCreateValidator
|
||||
func TestMsgCreateValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
pubkey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addrs[0], pks[0], coinPos, true},
|
||||
{"partial description", "", "", "c", "", addrs[0], pks[0], coinPos, true},
|
||||
{"empty description", "", "", "", "", addrs[0], pks[0], coinPos, false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, pks[0], coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", addrs[0], emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", addrs[0], pks[0], coinZero, false},
|
||||
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
|
||||
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
|
||||
{"wrong staking token", "a", "b", "c", "d", addrs[0], pks[0], coinPosNotAtoms, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgEditValidator
|
||||
func TestMsgEditValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addrs[0], true},
|
||||
{"partial description", "", "", "c", "", addrs[0], true},
|
||||
{"empty description", "", "", "", "", addrs[0], false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgEditValidator(tc.validatorAddr, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgDelegate
|
||||
func TestMsgDelegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", addrs[0], addrs[1], coinPos, true},
|
||||
{"self bond", addrs[0], addrs[0], coinPos, true},
|
||||
{"empty delegator", emptyAddr, addrs[0], coinPos, false},
|
||||
{"empty validator", addrs[0], emptyAddr, coinPos, false},
|
||||
{"empty bond", addrs[0], addrs[1], coinZero, false},
|
||||
{"negative bond", addrs[0], addrs[1], coinNeg, false},
|
||||
{"wrong staking token", addrs[0], addrs[1], coinPosNotAtoms, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgUnbond(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
shares string
|
||||
expectPass bool
|
||||
}{
|
||||
{"max unbond", addrs[0], addrs[1], "MAX", true},
|
||||
{"decimal unbond", addrs[0], addrs[1], "0.1", true},
|
||||
{"negative decimal unbond", addrs[0], addrs[1], "-0.1", false},
|
||||
{"zero unbond", addrs[0], addrs[1], "0.0", false},
|
||||
{"invalid decimal", addrs[0], addrs[0], "sunny", false},
|
||||
{"empty delegator", emptyAddr, addrs[0], "0.1", false},
|
||||
{"empty validator", addrs[0], emptyAddr, "0.1", false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgUnbond(tc.delegatorAddr, tc.validatorAddr, tc.shares)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO introduce with go-amino
|
||||
//func TestSerializeMsg(t *testing.T) {
|
||||
|
||||
//// make sure all types construct properly
|
||||
//bondAmt := 1234321
|
||||
//bond := sdk.Coin{Denom: "atom", Amount: int64(bondAmt)}
|
||||
|
||||
//tests := []struct {
|
||||
//tx sdk.Msg
|
||||
//}{
|
||||
//{NewMsgCreateValidator(addrs[0], pks[0], bond, Description{})},
|
||||
//{NewMsgEditValidator(addrs[0], Description{})},
|
||||
//{NewMsgDelegate(addrs[0], addrs[1], bond)},
|
||||
//{NewMsgUnbond(addrs[0], addrs[1], strconv.Itoa(bondAmt))},
|
||||
//}
|
||||
|
||||
//for i, tc := range tests {
|
||||
//var tx sdk.Tx
|
||||
//bs := wire.BinaryBytes(tc.tx)
|
||||
//err := wire.ReadBinaryBytes(bs, &tx)
|
||||
//if assert.NoError(t, err, "%d", i) {
|
||||
//assert.Equal(t, tc.tx, tx, "%d", i)
|
||||
//}
|
||||
//}
|
||||
//}
|
133
x/stake/pool.go
133
x/stake/pool.go
|
@ -1,133 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Pool - dynamic parameters of the current state
|
||||
type Pool struct {
|
||||
LooseUnbondedTokens sdk.Int `json:"loose_unbonded_tokens"` // tokens not associated with any validator
|
||||
UnbondedTokens sdk.Int `json:"unbonded_tokens"` // reserve of unbonded tokens held with validators
|
||||
UnbondingTokens sdk.Int `json:"unbonding_tokens"` // tokens moving from bonded to unbonded pool
|
||||
BondedTokens sdk.Int `json:"bonded_tokens"` // reserve of bonded tokens
|
||||
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
|
||||
UnbondingShares sdk.Rat `json:"unbonding_shares"` // shares moving from Bonded to Unbonded Pool
|
||||
BondedShares sdk.Rat `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
|
||||
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
|
||||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
|
||||
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
|
||||
|
||||
// Fee Related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations
|
||||
}
|
||||
|
||||
func (p Pool) equal(p2 Pool) bool {
|
||||
bz1 := msgCdc.MustMarshalBinary(&p)
|
||||
bz2 := msgCdc.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func InitialPool() Pool {
|
||||
return Pool{
|
||||
LooseUnbondedTokens: sdk.ZeroInt(),
|
||||
BondedTokens: sdk.ZeroInt(),
|
||||
UnbondingTokens: sdk.ZeroInt(),
|
||||
UnbondedTokens: sdk.ZeroInt(),
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
DateLastCommissionReset: 0,
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// Sum total of all staking tokens in the pool
|
||||
func (p Pool) TokenSupply() sdk.Int {
|
||||
return p.LooseUnbondedTokens.Add(p.UnbondedTokens).Add(p.UnbondingTokens).Add(p.BondedTokens)
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// get the bond ratio of the global state
|
||||
func (p Pool) bondedRatio() sdk.Rat {
|
||||
if p.TokenSupply().Sign() == 1 {
|
||||
return sdk.NewRatFromInt(p.BondedTokens, p.TokenSupply())
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// get the exchange rate of bonded token per issued share
|
||||
func (p Pool) bondedShareExRate() sdk.Rat {
|
||||
if p.BondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRatFromInt(p.BondedTokens).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonding tokens held in validators per issued share
|
||||
func (p Pool) unbondingShareExRate() sdk.Rat {
|
||||
if p.UnbondingShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRatFromInt(p.UnbondingTokens).Quo(p.UnbondingShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonded tokens held in validators per issued share
|
||||
func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRatFromInt(p.UnbondedTokens).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (p Pool) addTokensUnbonded(amount sdk.Int) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRatFromInt(amount).Quo(p.unbondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedSharesAmount)
|
||||
p.UnbondedTokens = p.UnbondedTokens.Add(amount)
|
||||
return p, NewUnbondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens sdk.Int) {
|
||||
removedTokens = sdk.NewIntFromBigInt(p.unbondedShareExRate().Mul(shares).EvaluateBig()) // (tokens/shares) * shares
|
||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||
p.UnbondedTokens = p.UnbondedTokens.Sub(removedTokens)
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonding(amount sdk.Int) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRatFromInt(amount).Quo(p.unbondingShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondingShares = p.UnbondingShares.Add(issuedSharesAmount)
|
||||
p.UnbondingTokens = p.UnbondingTokens.Add(amount)
|
||||
return p, NewUnbondingShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens sdk.Int) {
|
||||
removedTokens = sdk.NewIntFromBigInt(p.unbondingShareExRate().Mul(shares).EvaluateBig()) // (tokens/shares) * shares
|
||||
p.UnbondingShares = p.UnbondingShares.Sub(shares)
|
||||
p.UnbondingTokens = p.UnbondingTokens.Sub(removedTokens)
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensBonded(amount sdk.Int) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRatFromInt(amount).Quo(p.bondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.BondedShares = p.BondedShares.Add(issuedSharesAmount)
|
||||
p.BondedTokens = p.BondedTokens.Add(amount)
|
||||
return p, NewBondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens sdk.Int) {
|
||||
removedTokens = sdk.NewIntFromBigInt(p.bondedShareExRate().Mul(shares).EvaluateBig()) // (tokens/shares) * shares
|
||||
p.BondedShares = p.BondedShares.Sub(shares)
|
||||
p.BondedTokens = p.BondedTokens.Sub(removedTokens)
|
||||
return p, removedTokens
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestBondedRatio(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseUnbondedTokens = sdk.NewInt(1)
|
||||
pool.BondedTokens = sdk.NewInt(2)
|
||||
|
||||
// bonded pool / total supply
|
||||
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||
|
||||
// avoids divide-by-zero
|
||||
pool.LooseUnbondedTokens = sdk.NewInt(0)
|
||||
pool.BondedTokens = sdk.NewInt(0)
|
||||
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat())
|
||||
}
|
||||
|
||||
func TestBondedShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.BondedTokens = sdk.NewInt(3)
|
||||
pool.BondedShares = sdk.NewRat(10)
|
||||
|
||||
// bonded pool / bonded shares
|
||||
require.Equal(t, pool.bondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.BondedShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.bondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondingShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.UnbondingTokens = sdk.NewInt(3)
|
||||
pool.UnbondingShares = sdk.NewRat(10)
|
||||
|
||||
// unbonding pool / unbonding shares
|
||||
require.Equal(t, pool.unbondingShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.UnbondingShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.unbondingShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondedShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.UnbondedTokens = sdk.NewInt(3)
|
||||
pool.UnbondedShares = sdk.NewRat(10)
|
||||
|
||||
// unbonded pool / unbonded shares
|
||||
require.Equal(t, pool.unbondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.UnbondedShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.unbondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestAddTokensBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
poolB, sharesB := poolA.addTokensBonded(sdk.NewInt(10))
|
||||
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens.Add(sdk.NewInt(10)))
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRatFromInt(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
|
||||
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens.Sub(tokensB))
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRatFromInt(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestAddTokensUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
poolB, sharesB := poolA.addTokensUnbonded(sdk.NewInt(10))
|
||||
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to unbonded shares and unbonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens.Add(sdk.NewInt(10)))
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRatFromInt(poolB.UnbondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
|
||||
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to unbonded shares and bonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens.Sub(tokensB))
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRatFromInt(poolB.UnbondedTokens)))
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// nolint
|
||||
package stake
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/tags"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// keeper
|
||||
type Keeper = keeper.Keeper
|
||||
|
||||
var NewKeeper = keeper.NewKeeper
|
||||
|
||||
// types
|
||||
type Validator = types.Validator
|
||||
type Description = types.Description
|
||||
type Delegation = types.Delegation
|
||||
type UnbondingDelegation = types.UnbondingDelegation
|
||||
type Redelegation = types.Redelegation
|
||||
type Params = types.Params
|
||||
type Pool = types.Pool
|
||||
type PoolShares = types.PoolShares
|
||||
type MsgCreateValidator = types.MsgCreateValidator
|
||||
type MsgEditValidator = types.MsgEditValidator
|
||||
type MsgDelegate = types.MsgDelegate
|
||||
type MsgBeginUnbonding = types.MsgBeginUnbonding
|
||||
type MsgCompleteUnbonding = types.MsgCompleteUnbonding
|
||||
type MsgBeginRedelegate = types.MsgBeginRedelegate
|
||||
type MsgCompleteRedelegate = types.MsgCompleteRedelegate
|
||||
type GenesisState = types.GenesisState
|
||||
|
||||
var (
|
||||
GetValidatorKey = keeper.GetValidatorKey
|
||||
GetValidatorByPubKeyIndexKey = keeper.GetValidatorByPubKeyIndexKey
|
||||
GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey
|
||||
GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey
|
||||
GetTendermintUpdatesKey = keeper.GetTendermintUpdatesKey
|
||||
GetDelegationKey = keeper.GetDelegationKey
|
||||
GetDelegationsKey = keeper.GetDelegationsKey
|
||||
ParamKey = keeper.ParamKey
|
||||
PoolKey = keeper.PoolKey
|
||||
ValidatorsKey = keeper.ValidatorsKey
|
||||
ValidatorsByPubKeyIndexKey = keeper.ValidatorsByPubKeyIndexKey
|
||||
ValidatorsBondedIndexKey = keeper.ValidatorsBondedIndexKey
|
||||
ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey
|
||||
ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey
|
||||
ValidatorPowerCliffKey = keeper.ValidatorPowerCliffKey
|
||||
TendermintUpdatesKey = keeper.TendermintUpdatesKey
|
||||
DelegationKey = keeper.DelegationKey
|
||||
IntraTxCounterKey = keeper.IntraTxCounterKey
|
||||
GetUBDKey = keeper.GetUBDKey
|
||||
GetUBDByValIndexKey = keeper.GetUBDByValIndexKey
|
||||
GetUBDsKey = keeper.GetUBDsKey
|
||||
GetUBDsByValIndexKey = keeper.GetUBDsByValIndexKey
|
||||
GetREDKey = keeper.GetREDKey
|
||||
GetREDByValSrcIndexKey = keeper.GetREDByValSrcIndexKey
|
||||
GetREDByValDstIndexKey = keeper.GetREDByValDstIndexKey
|
||||
GetREDsKey = keeper.GetREDsKey
|
||||
GetREDsFromValSrcIndexKey = keeper.GetREDsFromValSrcIndexKey
|
||||
GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey
|
||||
GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey
|
||||
|
||||
DefaultParams = types.DefaultParams
|
||||
InitialPool = types.InitialPool
|
||||
NewUnbondedShares = types.NewUnbondedShares
|
||||
NewUnbondingShares = types.NewUnbondingShares
|
||||
NewBondedShares = types.NewBondedShares
|
||||
NewValidator = types.NewValidator
|
||||
NewDescription = types.NewDescription
|
||||
NewGenesisState = types.NewGenesisState
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
RegisterWire = types.RegisterWire
|
||||
|
||||
// messages
|
||||
NewMsgCreateValidator = types.NewMsgCreateValidator
|
||||
NewMsgEditValidator = types.NewMsgEditValidator
|
||||
NewMsgDelegate = types.NewMsgDelegate
|
||||
NewMsgBeginUnbonding = types.NewMsgBeginUnbonding
|
||||
NewMsgCompleteUnbonding = types.NewMsgCompleteUnbonding
|
||||
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate
|
||||
NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate
|
||||
)
|
||||
|
||||
// errors
|
||||
const (
|
||||
DefaultCodespace = types.DefaultCodespace
|
||||
CodeInvalidValidator = types.CodeInvalidValidator
|
||||
CodeInvalidDelegation = types.CodeInvalidDelegation
|
||||
CodeInvalidInput = types.CodeInvalidInput
|
||||
CodeValidatorJailed = types.CodeValidatorJailed
|
||||
CodeUnauthorized = types.CodeUnauthorized
|
||||
CodeInternal = types.CodeInternal
|
||||
CodeUnknownRequest = types.CodeUnknownRequest
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNilValidatorAddr = types.ErrNilValidatorAddr
|
||||
ErrNoValidatorFound = types.ErrNoValidatorFound
|
||||
ErrValidatorAlreadyExists = types.ErrValidatorAlreadyExists
|
||||
ErrValidatorRevoked = types.ErrValidatorRevoked
|
||||
ErrBadRemoveValidator = types.ErrBadRemoveValidator
|
||||
ErrDescriptionLength = types.ErrDescriptionLength
|
||||
ErrCommissionNegative = types.ErrCommissionNegative
|
||||
ErrCommissionHuge = types.ErrCommissionHuge
|
||||
|
||||
ErrNilDelegatorAddr = types.ErrNilDelegatorAddr
|
||||
ErrBadDenom = types.ErrBadDenom
|
||||
ErrBadDelegationAmount = types.ErrBadDelegationAmount
|
||||
ErrNoDelegation = types.ErrNoDelegation
|
||||
ErrBadDelegatorAddr = types.ErrBadDelegatorAddr
|
||||
ErrNoDelegatorForAddress = types.ErrNoDelegatorForAddress
|
||||
ErrInsufficientShares = types.ErrInsufficientShares
|
||||
ErrDelegationValidatorEmpty = types.ErrDelegationValidatorEmpty
|
||||
ErrNotEnoughDelegationShares = types.ErrNotEnoughDelegationShares
|
||||
ErrBadSharesAmount = types.ErrBadSharesAmount
|
||||
ErrBadSharesPercent = types.ErrBadSharesPercent
|
||||
|
||||
ErrNotMature = types.ErrNotMature
|
||||
ErrNoUnbondingDelegation = types.ErrNoUnbondingDelegation
|
||||
ErrNoRedelegation = types.ErrNoRedelegation
|
||||
ErrBadRedelegationDst = types.ErrBadRedelegationDst
|
||||
|
||||
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
|
||||
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
|
||||
ErrMissingSignature = types.ErrMissingSignature
|
||||
)
|
||||
|
||||
// tags
|
||||
var (
|
||||
ActionCreateValidator = tags.ActionCreateValidator
|
||||
ActionEditValidator = tags.ActionEditValidator
|
||||
ActionDelegate = tags.ActionDelegate
|
||||
ActionBeginUnbonding = tags.ActionBeginUnbonding
|
||||
ActionCompleteUnbonding = tags.ActionCompleteUnbonding
|
||||
ActionBeginRedelegation = tags.ActionBeginRedelegation
|
||||
ActionCompleteRedelegation = tags.ActionCompleteRedelegation
|
||||
|
||||
TagAction = tags.Action
|
||||
TagSrcValidator = tags.SrcValidator
|
||||
TagDstValidator = tags.DstValidator
|
||||
TagDelegator = tags.Delegator
|
||||
TagMoniker = tags.Moniker
|
||||
TagIdentity = tags.Identity
|
||||
)
|
|
@ -0,0 +1,23 @@
|
|||
// nolint
|
||||
package tags
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var (
|
||||
ActionCreateValidator = []byte("create-validator")
|
||||
ActionEditValidator = []byte("edit-validator")
|
||||
ActionDelegate = []byte("delegate")
|
||||
ActionBeginUnbonding = []byte("begin-unbonding")
|
||||
ActionCompleteUnbonding = []byte("complete-unbonding")
|
||||
ActionBeginRedelegation = []byte("begin-redelegation")
|
||||
ActionCompleteRedelegation = []byte("complete-redelegation")
|
||||
|
||||
Action = types.TagAction
|
||||
SrcValidator = types.TagSrcValidator
|
||||
DstValidator = types.TagDstValidator
|
||||
Delegator = types.TagDelegator
|
||||
Moniker = "moniker"
|
||||
Identity = "Identity"
|
||||
)
|
|
@ -0,0 +1,140 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Delegation 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.
|
||||
type Delegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
// two are equal
|
||||
func (d Delegation) Equal(d2 Delegation) bool {
|
||||
return bytes.Equal(d.DelegatorAddr, d2.DelegatorAddr) &&
|
||||
bytes.Equal(d.ValidatorAddr, d2.ValidatorAddr) &&
|
||||
d.Height == d2.Height &&
|
||||
d.Shares.Equal(d2.Shares)
|
||||
}
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Delegation = Delegation{}
|
||||
|
||||
// nolint - for sdk.Delegation
|
||||
func (d Delegation) GetDelegator() sdk.Address { return d.DelegatorAddr }
|
||||
func (d Delegation) GetValidator() sdk.Address { return d.ValidatorAddr }
|
||||
func (d Delegation) GetBondShares() sdk.Rat { return d.Shares }
|
||||
|
||||
//Human Friendly pretty printer
|
||||
func (d Delegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechVal, err := sdk.Bech32ifyAcc(d.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp := "Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
resp += fmt.Sprintf("Shares: %s", d.Shares.String())
|
||||
resp += fmt.Sprintf("Height: %d", d.Height)
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// element stored to represent the passive unbonding queue
|
||||
type UnbondingDelegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"` // delegator
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"` // validator unbonding from owner addr
|
||||
CreationHeight int64 `json:"creation_height"` // height which the unbonding took place
|
||||
MinTime int64 `json:"min_time"` // unix time for unbonding completion
|
||||
Balance sdk.Coin `json:"balance"` // atoms to receive at completion
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&d)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&d2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
//Human Friendly pretty printer
|
||||
func (d UnbondingDelegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechVal, err := sdk.Bech32ifyAcc(d.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp := "Unbonding Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
resp += fmt.Sprintf("Creation height: %v\n", d.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", d.MinTime)
|
||||
resp += fmt.Sprintf("Expected balance: %s", d.Balance.String())
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// element stored to represent the passive redelegation queue
|
||||
type Redelegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"` // delegator
|
||||
ValidatorSrcAddr sdk.Address `json:"validator_src_addr"` // validator redelegation source owner addr
|
||||
ValidatorDstAddr sdk.Address `json:"validator_dst_addr"` // validator redelegation destination owner addr
|
||||
CreationHeight int64 `json:"creation_height"` // height which the redelegation took place
|
||||
MinTime int64 `json:"min_time"` // unix time for redelegation completion
|
||||
SharesSrc sdk.Rat `json:"shares` // amount of source shares redelegating
|
||||
SharesDst sdk.Rat `json:"shares` // amount of destination shares redelegating
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (d Redelegation) Equal(d2 Redelegation) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&d)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&d2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
//Human Friendly pretty printer
|
||||
func (d Redelegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechValSrc, err := sdk.Bech32ifyAcc(d.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechValDst, err := sdk.Bech32ifyAcc(d.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp := "Redelegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Source Validator: %s\n", bechValSrc)
|
||||
resp += fmt.Sprintf("Destination Validator: %s\n", bechValDst)
|
||||
resp += fmt.Sprintf("Creation height: %v\n", d.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", d.MinTime)
|
||||
resp += fmt.Sprintf("Source shares: %s", d.SharesSrc.String())
|
||||
resp += fmt.Sprintf("Destination shares: %s", d.SharesDst.String())
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// nolint
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type CodeType = sdk.CodeType
|
||||
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 4
|
||||
|
||||
CodeInvalidValidator CodeType = 101
|
||||
CodeInvalidDelegation CodeType = 102
|
||||
CodeInvalidInput CodeType = 103
|
||||
CodeValidatorJailed CodeType = 104
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
//validator
|
||||
func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil")
|
||||
}
|
||||
func ErrNoValidatorFound(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrValidatorAlreadyExists(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator")
|
||||
}
|
||||
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently revoked")
|
||||
}
|
||||
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "error removing validator")
|
||||
}
|
||||
func ErrDescriptionLength(codespace sdk.CodespaceType, descriptor string, got, max int) sdk.Error {
|
||||
msg := fmt.Sprintf("bad description length for %v, got length %v, max is %v", descriptor, got, max)
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, msg)
|
||||
}
|
||||
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission must be positive")
|
||||
}
|
||||
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
|
||||
}
|
||||
|
||||
// delegation
|
||||
func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil")
|
||||
}
|
||||
func ErrBadDenom(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "invalid coin denomination")
|
||||
}
|
||||
func ErrBadDelegationAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "amount must be > 0")
|
||||
}
|
||||
func ErrNoDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no delegation for this (address, validator) pair")
|
||||
}
|
||||
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not exist for that address")
|
||||
}
|
||||
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not contain this delegation")
|
||||
}
|
||||
func ErrInsufficientShares(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "insufficient delegation shares")
|
||||
}
|
||||
func ErrDelegationValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "cannot delegate to an empty validator")
|
||||
}
|
||||
func ErrNotEnoughDelegationShares(codespace sdk.CodespaceType, shares string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, fmt.Sprintf("not enough shares only have %v", shares))
|
||||
}
|
||||
func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0")
|
||||
}
|
||||
func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1")
|
||||
}
|
||||
|
||||
// redelegation
|
||||
func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min int64) sdk.Error {
|
||||
msg := fmt.Sprintf("%v is not mature requires a min %v of %v, currently it is %v",
|
||||
operation, descriptor, got, min)
|
||||
return sdk.NewError(codespace, CodeUnauthorized, msg)
|
||||
}
|
||||
func ErrNoUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no unbonding delegation found")
|
||||
}
|
||||
func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found")
|
||||
}
|
||||
func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found")
|
||||
}
|
||||
func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation,
|
||||
"redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation")
|
||||
}
|
||||
|
||||
// messages
|
||||
func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided")
|
||||
}
|
||||
func ErrNeitherShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "neither shares amount nor shares percent provided")
|
||||
}
|
||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "missing signature")
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package types
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
Validators []Validator `json:"validators"`
|
||||
Bonds []Delegation `json:"bonds"`
|
||||
}
|
||||
|
||||
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
|
||||
return GenesisState{
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: InitialPool(),
|
||||
Params: DefaultParams(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// name to idetify transaction types
|
||||
const MsgType = "stake"
|
||||
|
||||
//Verify interface at compile time
|
||||
var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}
|
||||
var _, _ sdk.Msg = &MsgBeginUnbonding{}, &MsgCompleteUnbonding{}
|
||||
var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgCreateValidator - struct for unbonding transactions
|
||||
type MsgCreateValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
SelfDelegation sdk.Coin `json:"self_delegation"`
|
||||
}
|
||||
|
||||
func NewMsgCreateValidator(validatorAddr sdk.Address, pubkey crypto.PubKey,
|
||||
selfDelegation sdk.Coin, description Description) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
PubKey: pubkey,
|
||||
SelfDelegation: selfDelegation,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgCreateValidator) Type() string { return MsgType }
|
||||
func (msg MsgCreateValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCreateValidator) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
PubKey: sdk.MustBech32ifyValPub(msg.PubKey),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if !(msg.SelfDelegation.Amount.GT(sdk.ZeroInt())) {
|
||||
return ErrBadDelegationAmount(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "description must be included")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgEditValidator - struct for editing a validator
|
||||
type MsgEditValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
}
|
||||
|
||||
func NewMsgEditValidator(validatorAddr sdk.Address, description Description) MsgEditValidator {
|
||||
return MsgEditValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgEditValidator) Type() string { return MsgType }
|
||||
func (msg MsgEditValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgEditValidator) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgEditValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "nil validator address")
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgDelegate) Type() string { return MsgType }
|
||||
func (msg MsgDelegate) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.DelegatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgDelegate) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Bond: msg.Bond,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgDelegate) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if !(msg.Bond.Amount.GT(sdk.ZeroInt())) {
|
||||
return ErrBadDelegationAmount(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgBeginRedelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorSrcAddr sdk.Address `json:"validator_src_addr"`
|
||||
ValidatorDstAddr sdk.Address `json:"validator_dst_addr"`
|
||||
SharesAmount sdk.Rat `json:"shares_amount"`
|
||||
}
|
||||
|
||||
func NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, sharesAmount sdk.Rat) MsgBeginRedelegate {
|
||||
|
||||
return MsgBeginRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
SharesAmount: sharesAmount,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgBeginRedelegate) Type() string { return MsgType }
|
||||
func (msg MsgBeginRedelegate) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.DelegatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgBeginRedelegate) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"`
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"`
|
||||
SharesAmount string `json:"shares"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorSrcAddr: sdk.MustBech32ifyVal(msg.ValidatorSrcAddr),
|
||||
ValidatorDstAddr: sdk.MustBech32ifyVal(msg.ValidatorDstAddr),
|
||||
SharesAmount: msg.SharesAmount.String(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorSrcAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorDstAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.SharesAmount.LTE(sdk.ZeroRat()) {
|
||||
return ErrBadSharesAmount(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgCompleteRedelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorSrcAddr sdk.Address `json:"validator_source_addr"`
|
||||
ValidatorDstAddr sdk.Address `json:"validator_destination_addr"`
|
||||
}
|
||||
|
||||
func NewMsgCompleteRedelegate(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address) MsgCompleteRedelegate {
|
||||
|
||||
return MsgCompleteRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgCompleteRedelegate) Type() string { return MsgType }
|
||||
func (msg MsgCompleteRedelegate) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.DelegatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCompleteRedelegate) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"`
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorSrcAddr: sdk.MustBech32ifyVal(msg.ValidatorSrcAddr),
|
||||
ValidatorDstAddr: sdk.MustBech32ifyVal(msg.ValidatorDstAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgCompleteRedelegate) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorSrcAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorDstAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgBeginUnbonding - struct for unbonding transactions
|
||||
type MsgBeginUnbonding struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
SharesAmount sdk.Rat `json:"shares_amount"`
|
||||
}
|
||||
|
||||
func NewMsgBeginUnbonding(delegatorAddr, validatorAddr sdk.Address, sharesAmount sdk.Rat) MsgBeginUnbonding {
|
||||
return MsgBeginUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
SharesAmount: sharesAmount,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgBeginUnbonding) Type() string { return MsgType }
|
||||
func (msg MsgBeginUnbonding) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgBeginUnbonding) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
SharesAmount string `json:"shares_amount"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
SharesAmount: msg.SharesAmount.String(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.SharesAmount.LTE(sdk.ZeroRat()) {
|
||||
return ErrBadSharesAmount(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MsgCompleteUnbonding - struct for unbonding transactions
|
||||
type MsgCompleteUnbonding struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
}
|
||||
|
||||
func NewMsgCompleteUnbonding(delegatorAddr, validatorAddr sdk.Address) MsgCompleteUnbonding {
|
||||
return MsgCompleteUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgCompleteUnbonding) Type() string { return MsgType }
|
||||
func (msg MsgCompleteUnbonding) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCompleteUnbonding) GetSignBytes() []byte {
|
||||
b, err := MsgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_src_addr"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgCompleteUnbonding) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrNilValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
coinPos = sdk.Coin{"steak", sdk.NewInt(1000)}
|
||||
coinZero = sdk.Coin{"steak", sdk.NewInt(0)}
|
||||
coinNeg = sdk.Coin{"steak", sdk.NewInt(-10000)}
|
||||
)
|
||||
|
||||
// test ValidateBasic for MsgCreateValidator
|
||||
func TestMsgCreateValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
pubkey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addr1, pk1, coinPos, true},
|
||||
{"partial description", "", "", "c", "", addr1, pk1, coinPos, true},
|
||||
{"empty description", "", "", "", "", addr1, pk1, coinPos, false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, pk1, coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", addr1, emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", addr1, pk1, coinZero, false},
|
||||
{"negative bond", "a", "b", "c", "d", addr1, pk1, coinNeg, false},
|
||||
{"negative bond", "a", "b", "c", "d", addr1, pk1, coinNeg, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgEditValidator
|
||||
func TestMsgEditValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addr1, true},
|
||||
{"partial description", "", "", "c", "", addr1, true},
|
||||
{"empty description", "", "", "", "", addr1, false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgEditValidator(tc.validatorAddr, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgDelegate
|
||||
func TestMsgDelegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", addr1, addr2, coinPos, true},
|
||||
{"self bond", addr1, addr1, coinPos, true},
|
||||
{"empty delegator", emptyAddr, addr1, coinPos, false},
|
||||
{"empty validator", addr1, emptyAddr, coinPos, false},
|
||||
{"empty bond", addr1, addr2, coinZero, false},
|
||||
{"negative bond", addr1, addr2, coinNeg, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgBeginRedelegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorSrcAddr sdk.Address
|
||||
validatorDstAddr sdk.Address
|
||||
sharesAmount sdk.Rat
|
||||
expectPass bool
|
||||
}{
|
||||
{"regular", addr1, addr2, addr3, sdk.NewRat(1, 10), true},
|
||||
{"negative decimal", addr1, addr2, addr3, sdk.NewRat(-1, 10), false},
|
||||
{"zero amount", addr1, addr2, addr3, sdk.ZeroRat(), false},
|
||||
{"empty delegator", emptyAddr, addr1, addr3, sdk.NewRat(1, 10), false},
|
||||
{"empty source validator", addr1, emptyAddr, addr3, sdk.NewRat(1, 10), false},
|
||||
{"empty destination validator", addr1, addr2, emptyAddr, sdk.NewRat(1, 10), false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.sharesAmount)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgCompleteRedelegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorSrcAddr sdk.Address
|
||||
validatorDstAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"regular", addr1, addr2, addr3, true},
|
||||
{"empty delegator", emptyAddr, addr1, addr3, false},
|
||||
{"empty source validator", addr1, emptyAddr, addr3, false},
|
||||
{"empty destination validator", addr1, addr2, emptyAddr, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgCompleteRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgBeginUnbonding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
sharesAmount sdk.Rat
|
||||
expectPass bool
|
||||
}{
|
||||
{"regular", addr1, addr2, sdk.NewRat(1, 10), true},
|
||||
{"negative decimal", addr1, addr2, sdk.NewRat(-1, 10), false},
|
||||
{"zero amount", addr1, addr2, sdk.ZeroRat(), false},
|
||||
{"empty delegator", emptyAddr, addr1, sdk.NewRat(1, 10), false},
|
||||
{"empty validator", addr1, emptyAddr, sdk.NewRat(1, 10), false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgBeginUnbonding(tc.delegatorAddr, tc.validatorAddr, tc.sharesAmount)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgCompleteUnbonding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"regular", addr1, addr2, true},
|
||||
{"empty delegator", emptyAddr, addr1, false},
|
||||
{"empty validator", addr1, emptyAddr, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgCompleteUnbonding(tc.delegatorAddr, tc.validatorAddr)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO introduce with go-amino
|
||||
//func TestSerializeMsg(t *testing.T) {
|
||||
|
||||
//// make sure all types construct properly
|
||||
//bondAmt := 1234321
|
||||
//bond := sdk.Coin{Denom: "atom", Amount: int64(bondAmt)}
|
||||
|
||||
//tests := []struct {
|
||||
//tx sdk.Msg
|
||||
//}{
|
||||
//{NewMsgCreateValidator(addr1, pk1, bond, Description{})},
|
||||
//{NewMsgEditValidator(addr1, Description{})},
|
||||
//{NewMsgDelegate(addr1, addr2, bond)},
|
||||
//{NewMsgUnbond(addr1, addr2, strconv.Itoa(bondAmt))},
|
||||
//}
|
||||
|
||||
//for i, tc := range tests {
|
||||
//var tx sdk.Tx
|
||||
//bs := wire.BinaryBytes(tc.tx)
|
||||
//err := wire.ReadBinaryBytes(bs, &tx)
|
||||
//if assert.NoError(t, err, "%d", i) {
|
||||
//assert.Equal(t, tc.tx, tx, "%d", i)
|
||||
//}
|
||||
//}
|
||||
//}
|
|
@ -1,4 +1,4 @@
|
|||
package stake
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -13,13 +13,16 @@ type Params struct {
|
|||
InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate
|
||||
GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms
|
||||
|
||||
UnbondingTime int64 `json:"unbonding_time"`
|
||||
|
||||
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
|
||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
}
|
||||
|
||||
func (p Params) equal(p2 Params) bool {
|
||||
bz1 := msgCdc.MustMarshalBinary(&p)
|
||||
bz2 := msgCdc.MustMarshalBinary(&p2)
|
||||
// nolint
|
||||
func (p Params) Equal(p2 Params) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&p)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
|
@ -30,6 +33,7 @@ func DefaultParams() Params {
|
|||
InflationMax: sdk.NewRat(20, 100),
|
||||
InflationMin: sdk.NewRat(7, 100),
|
||||
GoalBonded: sdk.NewRat(67, 100),
|
||||
UnbondingTime: 60 * 60 * 24 * 3, // 3 weeks in seconds
|
||||
MaxValidators: 100,
|
||||
BondDenom: "steak",
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Pool - dynamic parameters of the current state
|
||||
type Pool struct {
|
||||
LooseTokens int64 `json:"loose_tokens"` // tokens not associated with any validator
|
||||
UnbondedTokens int64 `json:"unbonded_tokens"` // reserve of unbonded tokens held with validators
|
||||
UnbondingTokens int64 `json:"unbonding_tokens"` // tokens moving from bonded to unbonded pool
|
||||
BondedTokens int64 `json:"bonded_tokens"` // reserve of bonded tokens
|
||||
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
|
||||
UnbondingShares sdk.Rat `json:"unbonding_shares"` // shares moving from Bonded to Unbonded Pool
|
||||
BondedShares sdk.Rat `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
|
||||
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
|
||||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
|
||||
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
|
||||
|
||||
// Fee Related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (p Pool) Equal(p2 Pool) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&p)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func InitialPool() Pool {
|
||||
return Pool{
|
||||
LooseTokens: 0,
|
||||
BondedTokens: 0,
|
||||
UnbondingTokens: 0,
|
||||
UnbondedTokens: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
DateLastCommissionReset: 0,
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// Sum total of all staking tokens in the pool
|
||||
func (p Pool) TokenSupply() int64 {
|
||||
return p.LooseTokens + p.UnbondedTokens + p.UnbondingTokens + p.BondedTokens
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// get the bond ratio of the global state
|
||||
func (p Pool) BondedRatio() sdk.Rat {
|
||||
if p.TokenSupply() > 0 {
|
||||
return sdk.NewRat(p.BondedTokens, p.TokenSupply())
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// get the exchange rate of bonded token per issued share
|
||||
func (p Pool) BondedShareExRate() sdk.Rat {
|
||||
if p.BondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.BondedTokens).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonding tokens held in validators per issued share
|
||||
func (p Pool) UnbondingShareExRate() sdk.Rat {
|
||||
if p.UnbondingShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondingTokens).Quo(p.UnbondingShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonded tokens held in validators per issued share
|
||||
func (p Pool) UnbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondedTokens).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.UnbondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedSharesAmount)
|
||||
p.UnbondedTokens += amount
|
||||
p.LooseTokens -= amount
|
||||
if p.LooseTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, NewUnbondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.UnbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||
p.UnbondedTokens -= removedTokens
|
||||
p.LooseTokens += removedTokens
|
||||
if p.UnbondedTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: unbonded tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.UnbondingShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondingShares = p.UnbondingShares.Add(issuedSharesAmount)
|
||||
p.UnbondingTokens += amount
|
||||
p.LooseTokens -= amount
|
||||
if p.LooseTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, NewUnbondingShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.UnbondingShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondingShares = p.UnbondingShares.Sub(shares)
|
||||
p.UnbondingTokens -= removedTokens
|
||||
p.LooseTokens += removedTokens
|
||||
if p.UnbondedTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: unbonding tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.BondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.BondedShares = p.BondedShares.Add(issuedSharesAmount)
|
||||
p.BondedTokens += amount
|
||||
p.LooseTokens -= amount
|
||||
if p.LooseTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, NewBondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.BondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.BondedShares = p.BondedShares.Sub(shares)
|
||||
p.BondedTokens -= removedTokens
|
||||
p.LooseTokens += removedTokens
|
||||
if p.UnbondedTokens < 0 {
|
||||
panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p))
|
||||
}
|
||||
return p, removedTokens
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestBondedRatio(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 1
|
||||
pool.BondedTokens = 2
|
||||
|
||||
// bonded pool / total supply
|
||||
require.Equal(t, pool.BondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||
|
||||
// avoids divide-by-zero
|
||||
pool.LooseTokens = 0
|
||||
pool.BondedTokens = 0
|
||||
require.Equal(t, pool.BondedRatio(), sdk.ZeroRat())
|
||||
}
|
||||
|
||||
func TestBondedShareExRate(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.BondedTokens = 3
|
||||
pool.BondedShares = sdk.NewRat(10)
|
||||
|
||||
// bonded pool / bonded shares
|
||||
require.Equal(t, pool.BondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.BondedShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.BondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondingShareExRate(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.UnbondingTokens = 3
|
||||
pool.UnbondingShares = sdk.NewRat(10)
|
||||
|
||||
// unbonding pool / unbonding shares
|
||||
require.Equal(t, pool.UnbondingShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.UnbondingShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.UnbondingShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondedShareExRate(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.UnbondedTokens = 3
|
||||
pool.UnbondedShares = sdk.NewRat(10)
|
||||
|
||||
// unbonded pool / unbonded shares
|
||||
require.Equal(t, pool.UnbondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.UnbondedShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.UnbondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestAddTokensBonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
assert.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
||||
poolB, sharesB := poolA.addTokensBonded(10)
|
||||
assert.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens+10)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesBonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
assert.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
||||
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
|
||||
assert.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens-tokensB)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestAddTokensUnbonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
assert.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
||||
poolB, sharesB := poolA.addTokensUnbonded(10)
|
||||
assert.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to unbonded shares and unbonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens+10)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.UnbondedTokens = 10
|
||||
poolA.UnbondedShares = sdk.NewRat(10)
|
||||
assert.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
||||
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
|
||||
assert.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to unbonded shares and bonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens-tokensB)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package stake
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -70,10 +70,10 @@ func (s PoolShares) ToUnbonded(p Pool) PoolShares {
|
|||
var amount sdk.Rat
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.bondedShareExRate().Quo(p.unbondedShareExRate()) // (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondedShareExRate()) // (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondedshr/bondedshr = unbondedshr
|
||||
case sdk.Unbonding:
|
||||
exRate := p.unbondingShareExRate().Quo(p.unbondedShareExRate()) // (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
||||
exRate := p.UnbondingShareExRate().Quo(p.UnbondedShareExRate()) // (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
||||
amount = s.Amount.Mul(exRate) // unbondingshr*unbondedshr/unbondingshr = unbondedshr
|
||||
case sdk.Unbonded:
|
||||
amount = s.Amount
|
||||
|
@ -86,12 +86,12 @@ func (s PoolShares) ToUnbonding(p Pool) PoolShares {
|
|||
var amount sdk.Rat
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.bondedShareExRate().Quo(p.unbondingShareExRate()) // (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondingShareExRate()) // (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondingshr/bondedshr = unbondingshr
|
||||
case sdk.Unbonding:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonded:
|
||||
exRate := p.unbondedShareExRate().Quo(p.unbondingShareExRate()) // (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
||||
exRate := p.UnbondedShareExRate().Quo(p.UnbondingShareExRate()) // (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
||||
amount = s.Amount.Mul(exRate) // unbondedshr*unbondingshr/unbondedshr = unbondingshr
|
||||
}
|
||||
return NewUnbondingShares(amount)
|
||||
|
@ -104,10 +104,10 @@ func (s PoolShares) ToBonded(p Pool) PoolShares {
|
|||
case sdk.Bonded:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonding:
|
||||
exRate := p.unbondingShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
exRate := p.UnbondingShareExRate().Quo(p.BondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
case sdk.Unbonded:
|
||||
exRate := p.unbondedShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
exRate := p.UnbondedShareExRate().Quo(p.BondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
}
|
||||
return NewUnbondedShares(amount)
|
||||
|
@ -120,11 +120,11 @@ func (s PoolShares) ToBonded(p Pool) PoolShares {
|
|||
func (s PoolShares) Tokens(p Pool) sdk.Rat {
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
return p.bondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
return p.BondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
case sdk.Unbonding:
|
||||
return p.unbondingShareExRate().Mul(s.Amount)
|
||||
return p.UnbondingShareExRate().Mul(s.Amount)
|
||||
case sdk.Unbonded:
|
||||
return p.unbondedShareExRate().Mul(s.Amount)
|
||||
return p.UnbondedShareExRate().Mul(s.Amount)
|
||||
default:
|
||||
panic("unknown share kind")
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
// dummy pubkeys/addresses
|
||||
pk1 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk2 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk3 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
addr1 = pk1.Address()
|
||||
addr2 = pk2.Address()
|
||||
addr3 = pk3.Address()
|
||||
|
||||
emptyAddr sdk.Address
|
||||
emptyPubkey crypto.PubKey
|
||||
)
|
||||
|
||||
//______________________________________________________________
|
||||
|
||||
// any operation that transforms staking state
|
||||
// takes in RNG instance, pool, validator
|
||||
// returns updated pool, updated validator, delta tokens, descriptive message
|
||||
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, int64, string)
|
||||
|
||||
// operation: bond or unbond a validator depending on current status
|
||||
func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var msg string
|
||||
var newStatus sdk.BondStatus
|
||||
if val.Status() == sdk.Bonded {
|
||||
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Unbonded
|
||||
|
||||
} else if val.Status() == sdk.Unbonded {
|
||||
msg = fmt.Sprintf("sdk.Bonded previously unbonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Bonded
|
||||
}
|
||||
val, pool = val.UpdateStatus(pool, newStatus)
|
||||
return pool, val, 0, msg
|
||||
}
|
||||
|
||||
// operation: add a random number of tokens to a validator
|
||||
func OpAddTokens(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
tokens := int64(r.Int31n(1000))
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
val, pool, _ = val.AddTokensFromDel(pool, tokens)
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
return pool, val, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||
}
|
||||
|
||||
// operation: remove a random number of shares from a validator
|
||||
func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var shares sdk.Rat
|
||||
for {
|
||||
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||
if shares.LT(val.DelegatorShares) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Removed %v shares from validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
shares, val.Owner, val.Status(), val.PoolShares, val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
|
||||
val, pool, tokens := val.RemoveDelShares(pool, shares)
|
||||
return pool, val, tokens, msg
|
||||
}
|
||||
|
||||
// pick a random staking operation
|
||||
func RandomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
OpBondOrUnbond,
|
||||
OpAddTokens,
|
||||
OpRemoveShares,
|
||||
}
|
||||
r.Shuffle(len(operations), func(i, j int) {
|
||||
operations[i], operations[j] = operations[j], operations[i]
|
||||
})
|
||||
return operations[0]
|
||||
}
|
||||
|
||||
// ensure invariants that should always be true are true
|
||||
func AssertInvariants(t *testing.T, msg string,
|
||||
pOrig Pool, cOrig []Validator, pMod Pool, vMods []Validator, tokens int64) {
|
||||
|
||||
// total tokens conserved
|
||||
require.Equal(t,
|
||||
pOrig.UnbondedTokens+pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens+pMod.BondedTokens+tokens,
|
||||
"Tokens not conserved - msg: %v\n, pOrig.PoolShares.Bonded(): %v, pOrig.PoolShares.Unbonded(): %v, pMod.PoolShares.Bonded(): %v, pMod.PoolShares.Unbonded(): %v, pOrig.UnbondedTokens: %v, pOrig.BondedTokens: %v, pMod.UnbondedTokens: %v, pMod.BondedTokens: %v, tokens: %v\n",
|
||||
msg,
|
||||
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||
pMod.BondedShares, pMod.UnbondedShares,
|
||||
pOrig.UnbondedTokens, pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens, pMod.BondedTokens, tokens)
|
||||
|
||||
// nonnegative bonded shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative unbonded shares
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative bonded ex rate
|
||||
require.False(t, pMod.BondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative BondedShareExRate: %d",
|
||||
msg, pMod.BondedShareExRate().Evaluate())
|
||||
|
||||
// nonnegative unbonded ex rate
|
||||
require.False(t, pMod.UnbondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative UnbondedShareExRate: %d",
|
||||
msg, pMod.UnbondedShareExRate().Evaluate())
|
||||
|
||||
for _, vMod := range vMods {
|
||||
|
||||
// nonnegative ex rate
|
||||
require.False(t, vMod.DelegatorShareExRate(pMod).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative poolShares
|
||||
require.False(t, vMod.PoolShares.Bonded().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.PoolShares.Bonded(): %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShares,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative delShares
|
||||
require.False(t, vMod.DelegatorShares.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %v (validator.PoolShares.Bonded(): %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShares,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
// TODO refactor this random setup
|
||||
|
||||
// generate a random validator
|
||||
func randomValidator(r *rand.Rand, i int) Validator {
|
||||
|
||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
|
||||
var pShares PoolShares
|
||||
if r.Float64() < float64(0.5) {
|
||||
pShares = NewBondedShares(poolSharesAmt)
|
||||
} else {
|
||||
pShares = NewUnbondedShares(poolSharesAmt)
|
||||
}
|
||||
return Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: pShares,
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 100000
|
||||
|
||||
validators := make([]Validator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validator := randomValidator(r, i)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
||||
pool.BondedTokens += validator.PoolShares.Bonded().Evaluate()
|
||||
} else if validator.Status() == sdk.Unbonded {
|
||||
pool.UnbondedShares = pool.UnbondedShares.Add(validator.PoolShares.Unbonded())
|
||||
pool.UnbondedTokens += validator.PoolShares.Unbonded().Evaluate()
|
||||
}
|
||||
validators[i] = validator
|
||||
}
|
||||
return pool, validators
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
package stake
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Validator defines the total amount of bond shares and their exchange rate to
|
||||
|
@ -40,9 +41,6 @@ type Validator struct {
|
|||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
||||
}
|
||||
|
||||
// Validators - list of Validators
|
||||
type Validators []Validator
|
||||
|
||||
// NewValidator - initialize a new validator
|
||||
func NewValidator(owner sdk.Address, pubKey crypto.PubKey, description Description) Validator {
|
||||
return Validator{
|
||||
|
@ -64,7 +62,7 @@ func NewValidator(owner sdk.Address, pubKey crypto.PubKey, description Descripti
|
|||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
func (v Validator) equal(c2 Validator) bool {
|
||||
func (v Validator) Equal(c2 Validator) bool {
|
||||
return v.PubKey.Equals(c2.PubKey) &&
|
||||
bytes.Equal(v.Owner, c2.Owner) &&
|
||||
v.PoolShares.Equal(c2.PoolShares) &&
|
||||
|
@ -97,10 +95,47 @@ func NewDescription(moniker, identity, website, details string) Description {
|
|||
}
|
||||
}
|
||||
|
||||
//XXX updateDescription function which enforce limit to number of description characters
|
||||
// update the description based on input
|
||||
func (d Description) UpdateDescription(d2 Description) (Description, sdk.Error) {
|
||||
if d.Moniker == "[do-not-modify]" {
|
||||
d2.Moniker = d.Moniker
|
||||
}
|
||||
if d.Identity == "[do-not-modify]" {
|
||||
d2.Identity = d.Identity
|
||||
}
|
||||
if d.Website == "[do-not-modify]" {
|
||||
d2.Website = d.Website
|
||||
}
|
||||
if d.Details == "[do-not-modify]" {
|
||||
d2.Details = d.Details
|
||||
}
|
||||
return Description{
|
||||
Moniker: d2.Moniker,
|
||||
Identity: d2.Identity,
|
||||
Website: d2.Website,
|
||||
Details: d2.Details,
|
||||
}.EnsureLength()
|
||||
}
|
||||
|
||||
// ensure the length of the description
|
||||
func (d Description) EnsureLength() (Description, sdk.Error) {
|
||||
if len(d.Moniker) > 70 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "moniker", len(d.Moniker), 70)
|
||||
}
|
||||
if len(d.Identity) > 3000 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "identity", len(d.Identity), 3000)
|
||||
}
|
||||
if len(d.Website) > 140 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "website", len(d.Website), 140)
|
||||
}
|
||||
if len(d.Details) > 280 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "details", len(d.Details), 280)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
||||
func (v Validator) ABCIValidator(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
||||
Power: v.PoolShares.Bonded().Evaluate(),
|
||||
|
@ -109,7 +144,7 @@ func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
|||
|
||||
// abci validator from stake validator type
|
||||
// with zero power used for validator updates
|
||||
func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
|
||||
func (v Validator) ABCIValidatorZero(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
||||
Power: 0,
|
||||
|
@ -123,7 +158,7 @@ func (v Validator) Status() sdk.BondStatus {
|
|||
|
||||
// update the location of the shares within a validator if its bond status has changed
|
||||
func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) {
|
||||
var tokens sdk.Int
|
||||
var tokens int64
|
||||
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
|
@ -159,8 +194,8 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
|
|||
// Remove pool shares
|
||||
// Returns corresponding tokens, which could be burned (e.g. when slashing
|
||||
// a validator) or redistributed elsewhere
|
||||
func (v Validator) removePoolShares(pool Pool, poolShares sdk.Rat) (Validator, Pool, sdk.Int) {
|
||||
var tokens sdk.Int
|
||||
func (v Validator) RemovePoolShares(pool Pool, poolShares sdk.Rat) (Validator, Pool, int64) {
|
||||
var tokens int64
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
pool, tokens = pool.removeSharesUnbonded(poolShares)
|
||||
|
@ -173,7 +208,7 @@ func (v Validator) removePoolShares(pool Pool, poolShares sdk.Rat) (Validator, P
|
|||
return v, pool, tokens
|
||||
}
|
||||
|
||||
// XXX TEST
|
||||
// TODO remove should only be tokens
|
||||
// get the power or potential power for a validator
|
||||
// if bonded, the power is the BondedShares
|
||||
// if not bonded, the power is the amount of bonded shares which the
|
||||
|
@ -184,10 +219,9 @@ func (v Validator) EquivalentBondedShares(pool Pool) (eqBondedShares sdk.Rat) {
|
|||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// XXX Audit this function further to make sure it's correct
|
||||
// add tokens to a validator
|
||||
func (v Validator) addTokensFromDel(pool Pool,
|
||||
amount sdk.Int) (validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat) {
|
||||
func (v Validator) AddTokensFromDel(pool Pool,
|
||||
amount int64) (validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat) {
|
||||
|
||||
exRate := v.DelegatorShareExRate(pool) // bshr/delshr
|
||||
|
||||
|
@ -212,8 +246,8 @@ func (v Validator) addTokensFromDel(pool Pool,
|
|||
|
||||
// remove delegator shares from a validator
|
||||
// NOTE this function assumes the shares have already been updated for the validator status
|
||||
func (v Validator) removeDelShares(pool Pool,
|
||||
delShares sdk.Rat) (validator2 Validator, p2 Pool, createdCoins sdk.Int) {
|
||||
func (v Validator) RemoveDelShares(pool Pool,
|
||||
delShares sdk.Rat) (validator2 Validator, p2 Pool, createdCoins int64) {
|
||||
|
||||
amount := v.DelegatorShareExRate(pool).Mul(delShares)
|
||||
eqBondedSharesToRemove := NewBondedShares(amount)
|
|
@ -0,0 +1,232 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestAddTokensValidatorBonded(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Bonded()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonding(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonding()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonded(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonded()))
|
||||
}
|
||||
|
||||
// TODO refactor to make simpler like the AddToken tests above
|
||||
func TestRemoveDelShares(t *testing.T) {
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
valA := Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
||||
DelegatorShares: sdk.NewRat(100),
|
||||
}
|
||||
poolA.BondedTokens = valA.PoolShares.Bonded().Evaluate()
|
||||
poolA.BondedShares = valA.PoolShares.Bonded()
|
||||
assert.Equal(t, valA.DelegatorShareExRate(poolA), sdk.OneRat())
|
||||
assert.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
||||
valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewRat(10))
|
||||
|
||||
// coins were created
|
||||
assert.Equal(t, coinsB, int64(10))
|
||||
// pool shares were removed
|
||||
assert.Equal(t, valB.PoolShares.Bonded(), valA.PoolShares.Bonded().Sub(sdk.NewRat(10).Mul(valA.DelegatorShareExRate(poolA))))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedTokens+poolB.BondedTokens+coinsB, poolA.UnbondedTokens+poolA.BondedTokens)
|
||||
|
||||
// specific case from random tests
|
||||
poolShares := sdk.NewRat(5102)
|
||||
delShares := sdk.NewRat(115)
|
||||
val := Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
BondedShares: sdk.NewRat(248305),
|
||||
UnbondedShares: sdk.NewRat(232147),
|
||||
BondedTokens: 248305,
|
||||
UnbondedTokens: 232147,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
shares := sdk.NewRat(29)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||
_, newPool, tokens := val.RemoveDelShares(pool, shares)
|
||||
require.Equal(t,
|
||||
tokens+newPool.UnbondedTokens+newPool.BondedTokens,
|
||||
pool.BondedTokens+pool.UnbondedTokens,
|
||||
"Tokens were not conserved: %s", msg)
|
||||
}
|
||||
|
||||
func TestUpdateStatus(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 100
|
||||
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
val, pool, _ = val.AddTokensFromDel(pool, 100)
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(100), pool.UnbondedTokens)
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens)
|
||||
assert.Equal(t, int64(100), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens)
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
assert.Equal(t, int64(100), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(100), pool.BondedTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens)
|
||||
}
|
||||
|
||||
func TestPossibleOverflow(t *testing.T) {
|
||||
poolShares := sdk.NewRat(2159)
|
||||
delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||
val := Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
LooseTokens: 100,
|
||||
BondedShares: poolShares,
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedTokens: poolShares.Evaluate(),
|
||||
UnbondedTokens: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
tokens := int64(71)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newValidator, _, _ := val.AddTokensFromDel(pool, tokens)
|
||||
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
require.False(t, newValidator.DelegatorShareExRate(pool).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
|
||||
msg, newValidator.DelegatorShareExRate(pool))
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random single-validator state, assert invariants hold
|
||||
func TestSingleValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(41))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := RandomSetup(r, 1)
|
||||
require.Equal(t, 1, len(validatorsOrig))
|
||||
|
||||
// sanity check
|
||||
AssertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
poolMod, validatorMod, tokens, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[0])
|
||||
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
require.Equal(t, 1, len(validatorsOrig), "j %v", j)
|
||||
require.Equal(t, 1, len(validatorsMod), "j %v", j)
|
||||
validatorsMod[0] = validatorMod
|
||||
|
||||
AssertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random multi-validator state, assert invariants hold
|
||||
func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(42))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := RandomSetup(r, 100)
|
||||
|
||||
AssertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
index := int(r.Int31n(int32(len(validatorsOrig))))
|
||||
poolMod, validatorMod, tokens, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[index])
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
validatorsMod[index] = validatorMod
|
||||
|
||||
AssertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Register concrete types on wire codec
|
||||
func RegisterWire(cdc *wire.Codec) {
|
||||
cdc.RegisterConcrete(MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil)
|
||||
cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil)
|
||||
cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil)
|
||||
cdc.RegisterConcrete(MsgBeginUnbonding{}, "cosmos-sdk/BeginUnbonding", nil)
|
||||
cdc.RegisterConcrete(MsgCompleteUnbonding{}, "cosmos-sdk/CompleteUnbonding", nil)
|
||||
cdc.RegisterConcrete(MsgBeginRedelegate{}, "cosmos-sdk/BeginRedelegate", nil)
|
||||
cdc.RegisterConcrete(MsgCompleteRedelegate{}, "cosmos-sdk/CompleteRedelegate", nil)
|
||||
}
|
||||
|
||||
// generic sealed codec to be used throughout sdk
|
||||
var MsgCdc *wire.Codec
|
||||
|
||||
func init() {
|
||||
cdc := wire.NewCodec()
|
||||
RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
MsgCdc = cdc
|
||||
//MsgCdc = cdc.Seal() //TODO use when upgraded to go-amino 0.9.10
|
||||
}
|
|
@ -1,408 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddTokensValidatorBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, sdk.NewInt(10))
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Bonded()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonding(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, sdk.NewInt(10))
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonding()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, sdk.NewInt(10))
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonded()))
|
||||
}
|
||||
|
||||
// TODO refactor to make simpler like the AddToken tests above
|
||||
func TestRemoveShares(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
valA := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(sdk.NewRat(9)),
|
||||
DelegatorShares: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedTokens = valA.PoolShares.Bonded().EvaluateInt()
|
||||
poolA.BondedShares = valA.PoolShares.Bonded()
|
||||
assert.Equal(t, valA.DelegatorShareExRate(poolA), sdk.OneRat())
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
valB, poolB, coinsB := valA.removeDelShares(poolA, sdk.NewRat(10))
|
||||
|
||||
// coins were created
|
||||
assert.Equal(t, coinsB.Int64(), int64(10))
|
||||
// pool shares were removed
|
||||
assert.Equal(t, valB.PoolShares.Bonded(), valA.PoolShares.Bonded().Sub(sdk.NewRat(10).Mul(valA.DelegatorShareExRate(poolA))))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedTokens.Add(poolB.BondedTokens).Add(coinsB), poolA.UnbondedTokens.Add(poolA.BondedTokens))
|
||||
|
||||
// specific case from random tests
|
||||
poolShares := sdk.NewRat(5102)
|
||||
delShares := sdk.NewRat(115)
|
||||
val := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
BondedShares: sdk.NewRat(248305),
|
||||
UnbondedShares: sdk.NewRat(232147),
|
||||
BondedTokens: sdk.NewInt(248305),
|
||||
UnbondedTokens: sdk.NewInt(232147),
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
shares := sdk.NewRat(29)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||
_, newPool, tokens := val.removeDelShares(pool, shares)
|
||||
require.Equal(t,
|
||||
tokens.Add(newPool.UnbondedTokens).Add(newPool.BondedTokens),
|
||||
pool.BondedTokens.Add(pool.UnbondedTokens),
|
||||
"Tokens were not conserved: %s", msg)
|
||||
}
|
||||
|
||||
func TestUpdateStatus(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool, _ = val.addTokensFromDel(pool, sdk.NewInt(100))
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens.Int64())
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens.Int64())
|
||||
assert.Equal(t, int64(100), pool.UnbondedTokens.Int64())
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens.Int64())
|
||||
assert.Equal(t, int64(100), pool.UnbondingTokens.Int64())
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens.Int64())
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
assert.Equal(t, int64(100), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(100), pool.BondedTokens.Int64())
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens.Int64())
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens.Int64())
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
// TODO refactor this random setup
|
||||
|
||||
// generate a random validator
|
||||
func randomValidator(r *rand.Rand, i int) Validator {
|
||||
|
||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
|
||||
var pShares PoolShares
|
||||
if r.Float64() < float64(0.5) {
|
||||
pShares = NewBondedShares(poolSharesAmt)
|
||||
} else {
|
||||
pShares = NewUnbondedShares(poolSharesAmt)
|
||||
}
|
||||
return Validator{
|
||||
Owner: addrs[i],
|
||||
PubKey: pks[i],
|
||||
PoolShares: pShares,
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
func randomSetup(r *rand.Rand, numValidators int) (Pool, Validators) {
|
||||
pool := InitialPool()
|
||||
|
||||
validators := make([]Validator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validator := randomValidator(r, i)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
||||
pool.BondedTokens = pool.BondedTokens.Add(validator.PoolShares.Bonded().EvaluateInt())
|
||||
} else if validator.Status() == sdk.Unbonded {
|
||||
pool.UnbondedShares = pool.UnbondedShares.Add(validator.PoolShares.Unbonded())
|
||||
pool.UnbondedTokens = pool.UnbondedTokens.Add(validator.PoolShares.Unbonded().EvaluateInt())
|
||||
}
|
||||
validators[i] = validator
|
||||
}
|
||||
return pool, validators
|
||||
}
|
||||
|
||||
// any operation that transforms staking state
|
||||
// takes in RNG instance, pool, validator
|
||||
// returns updated pool, updated validator, delta tokens, descriptive message
|
||||
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, sdk.Int, string)
|
||||
|
||||
// operation: bond or unbond a validator depending on current status
|
||||
func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, sdk.Int, string) {
|
||||
var msg string
|
||||
var newStatus sdk.BondStatus
|
||||
if val.Status() == sdk.Bonded {
|
||||
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Unbonded
|
||||
|
||||
} else if val.Status() == sdk.Unbonded {
|
||||
msg = fmt.Sprintf("sdk.Bonded previously unbonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Bonded
|
||||
}
|
||||
val, pool = val.UpdateStatus(pool, newStatus)
|
||||
return pool, val, sdk.ZeroInt(), msg
|
||||
}
|
||||
|
||||
// operation: add a random number of tokens to a validator
|
||||
func OpAddTokens(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, sdk.Int, string) {
|
||||
tokens := sdk.NewInt(int64(r.Int31n(1000)))
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
val, pool, _ = val.addTokensFromDel(pool, tokens)
|
||||
msg = fmt.Sprintf("Added %v tokens to %s", tokens, msg)
|
||||
return pool, val, tokens.Neg(), msg // tokens are removed so for accounting must be negative
|
||||
}
|
||||
|
||||
// operation: remove a random number of shares from a validator
|
||||
func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, sdk.Int, string) {
|
||||
var shares sdk.Rat
|
||||
for {
|
||||
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||
if shares.LT(val.DelegatorShares) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Removed %v shares from validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
shares, val.Owner, val.Status(), val.PoolShares, val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
|
||||
val, pool, tokens := val.removeDelShares(pool, shares)
|
||||
return pool, val, tokens, msg
|
||||
}
|
||||
|
||||
// pick a random staking operation
|
||||
func randomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
OpBondOrUnbond,
|
||||
OpAddTokens,
|
||||
OpRemoveShares,
|
||||
}
|
||||
r.Shuffle(len(operations), func(i, j int) {
|
||||
operations[i], operations[j] = operations[j], operations[i]
|
||||
})
|
||||
return operations[0]
|
||||
}
|
||||
|
||||
// ensure invariants that should always be true are true
|
||||
func assertInvariants(t *testing.T, msg string,
|
||||
pOrig Pool, cOrig Validators, pMod Pool, vMods Validators, tokens sdk.Int) {
|
||||
|
||||
// total tokens conserved
|
||||
require.Equal(t,
|
||||
pOrig.UnbondedTokens.Add(pOrig.BondedTokens),
|
||||
pMod.UnbondedTokens.Add(pMod.BondedTokens).Add(tokens),
|
||||
"Tokens not conserved - msg: %v\n, pOrig.PoolShares.Bonded(): %v, pOrig.PoolShares.Unbonded(): %v, pMod.PoolShares.Bonded(): %v, pMod.PoolShares.Unbonded(): %v, pOrig.UnbondedTokens: %v, pOrig.BondedTokens: %v, pMod.UnbondedTokens: %v, pMod.BondedTokens: %v, tokens: %v\n",
|
||||
msg,
|
||||
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||
pMod.BondedShares, pMod.UnbondedShares,
|
||||
pOrig.UnbondedTokens, pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens, pMod.BondedTokens, tokens)
|
||||
|
||||
// nonnegative bonded shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative unbonded shares
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative bonded ex rate
|
||||
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||
msg, pMod.bondedShareExRate().Evaluate())
|
||||
|
||||
// nonnegative unbonded ex rate
|
||||
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||
msg, pMod.unbondedShareExRate().Evaluate())
|
||||
|
||||
for _, vMod := range vMods {
|
||||
|
||||
// nonnegative ex rate
|
||||
require.False(t, vMod.DelegatorShareExRate(pMod).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative poolShares
|
||||
require.False(t, vMod.PoolShares.Bonded().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.PoolShares.Bonded(): %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShares,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative delShares
|
||||
require.False(t, vMod.DelegatorShares.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %v (validator.PoolShares.Bonded(): %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShares,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPossibleOverflow(t *testing.T) {
|
||||
poolShares := sdk.NewRat(2159)
|
||||
delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||
val := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
BondedShares: poolShares,
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedTokens: poolShares.EvaluateInt(),
|
||||
UnbondedTokens: sdk.ZeroInt(),
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
tokens := sdk.NewInt(71)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newValidator, _, _ := val.addTokensFromDel(pool, tokens)
|
||||
|
||||
msg = fmt.Sprintf("Added %v tokens to %s", tokens, msg)
|
||||
require.False(t, newValidator.DelegatorShareExRate(pool).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
|
||||
msg, newValidator.DelegatorShareExRate(pool))
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random single-validator state, assert invariants hold
|
||||
func TestSingleValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(41))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := randomSetup(r, 1)
|
||||
require.Equal(t, 1, len(validatorsOrig))
|
||||
|
||||
// sanity check
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, sdk.ZeroInt())
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[0])
|
||||
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
require.Equal(t, 1, len(validatorsOrig), "j %v", j)
|
||||
require.Equal(t, 1, len(validatorsMod), "j %v", j)
|
||||
validatorsMod[0] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random multi-validator state, assert invariants hold
|
||||
func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(42))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := randomSetup(r, 100)
|
||||
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, sdk.ZeroInt())
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
index := int(r.Int31n(int32(len(validatorsOrig))))
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[index])
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
validatorsMod[index] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// keeper to view information & slash validators
|
||||
// will be used by governance module
|
||||
type ViewSlashKeeper struct {
|
||||
keeper Keeper
|
||||
}
|
||||
|
||||
// NewViewSlashKeeper creates a keeper restricted to
|
||||
// viewing information & slashing validators
|
||||
func NewViewSlashKeeper(k Keeper) ViewSlashKeeper {
|
||||
return ViewSlashKeeper{k}
|
||||
}
|
||||
|
||||
// load a delegator bond
|
||||
func (v ViewSlashKeeper) GetDelegation(ctx sdk.Context,
|
||||
delegatorAddr sdk.Address, validatorAddr sdk.Address) (bond Delegation, found bool) {
|
||||
return v.keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
}
|
||||
|
||||
// load n delegator bonds
|
||||
func (v ViewSlashKeeper) GetDelegations(ctx sdk.Context,
|
||||
delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
|
||||
return v.keeper.GetDelegations(ctx, delegator, maxRetrieve)
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// tests GetDelegation, GetDelegations
|
||||
func TestViewSlashBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
//construct the validators
|
||||
amts := []int64{9, 8, 7}
|
||||
var validators [3]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = Validator{
|
||||
Owner: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
PoolShares: NewUnbondedShares(sdk.NewRat(amt)),
|
||||
DelegatorShares: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// first add a validators[0] to delegate too
|
||||
keeper.updateValidator(ctx, validators[0])
|
||||
|
||||
bond1to1 := Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
viewSlashKeeper := NewViewSlashKeeper(keeper)
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found = viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// add some more records
|
||||
keeper.updateValidator(ctx, validators[1])
|
||||
keeper.updateValidator(ctx, validators[2])
|
||||
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegation(ctx, bond1to2)
|
||||
keeper.setDelegation(ctx, bond1to3)
|
||||
keeper.setDelegation(ctx, bond2to1)
|
||||
keeper.setDelegation(ctx, bond2to2)
|
||||
keeper.setDelegation(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := viewSlashKeeper.GetDelegations(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond1to1.equal(resBonds[0]))
|
||||
assert.True(t, bond1to2.equal(resBonds[1]))
|
||||
assert.True(t, bond1to3.equal(resBonds[2]))
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond2to1.equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.equal(resBonds[1]))
|
||||
assert.True(t, bond2to3.equal(resBonds[2]))
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Register concrete types on wire codec
|
||||
func RegisterWire(cdc *wire.Codec) {
|
||||
cdc.RegisterConcrete(MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil)
|
||||
cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil)
|
||||
cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil)
|
||||
cdc.RegisterConcrete(MsgUnbond{}, "cosmos-sdk/MsgUnbond", nil)
|
||||
}
|
||||
|
||||
var msgCdc = wire.NewCodec()
|
||||
|
||||
func init() {
|
||||
RegisterWire(msgCdc)
|
||||
wire.RegisterCrypto(msgCdc)
|
||||
}
|
Loading…
Reference in New Issue