commit
4f0acc4df8
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 0.13.1 (April 3, 2018)
|
||||
|
||||
BUG FIXES
|
||||
|
||||
* [x/ibc] Fix CLI and relay for IBC txs
|
||||
* [x/stake] Various fixes/improvements
|
||||
|
||||
## 0.13.0 (April 2, 2018)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
@ -7,7 +14,7 @@ BREAKING CHANGES
|
|||
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
|
||||
* [basecoin] NewBasecoinApp takes a `map[string]dbm.DB` as temporary measure
|
||||
to allow mounting multiple stores with their own DB until they can share one
|
||||
* [staking] Renamed to `simplestake`
|
||||
* [x/staking] Renamed to `simplestake`
|
||||
* [builder] Functions don't take `passphrase` as argument
|
||||
* [server] GenAppState returns generated seed and address
|
||||
* [basecoind] `init` command outputs JSON of everything necessary for testnet
|
||||
|
@ -18,7 +25,7 @@ FEATURES
|
|||
|
||||
* [types] `Coin` supports direct arithmetic operations
|
||||
* [basecoind] Add `show_validator` and `show_node_id` commands
|
||||
* [staking] Initial merge of full staking module!
|
||||
* [x/stake] Initial merge of full staking module!
|
||||
* [democoin] New example application to demo custom modules
|
||||
|
||||
IMPROVEMENTS
|
||||
|
@ -42,9 +49,9 @@ BREAKING CHANGES
|
|||
* [types] Replace tx.GetFeePayer with FeePayer(tx) - returns the first signer
|
||||
* [types] NewStdTx takes the Fee
|
||||
* [types] ParseAccount -> AccountDecoder; ErrTxParse -> ErrTxDecoder
|
||||
* [auth] AnteHandler deducts fees
|
||||
* [bank] Move some errors to `types`
|
||||
* [bank] Remove sequence and signature from Input
|
||||
* [x/auth] AnteHandler deducts fees
|
||||
* [x/bank] Move some errors to `types`
|
||||
* [x/bank] Remove sequence and signature from Input
|
||||
|
||||
FEATURES
|
||||
|
||||
|
@ -68,8 +75,8 @@ IMPROVEMENTS
|
|||
* [specs] Staking
|
||||
|
||||
BUG FIXES
|
||||
* [auth] Fix setting pubkey on new account
|
||||
* [auth] Require signatures to include the sequences
|
||||
* [x/auth] Fix setting pubkey on new account
|
||||
* [x/auth] Require signatures to include the sequences
|
||||
* [baseapp] Dont panic on nil handler
|
||||
* [basecoin] Check for empty bytes in account and tx
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ package version
|
|||
|
||||
const Maj = "0"
|
||||
const Min = "13"
|
||||
const Fix = "0"
|
||||
const Fix = "1"
|
||||
|
||||
const Version = "0.13.0"
|
||||
const Version = "0.13.1"
|
||||
|
||||
// GitCommit set by build flags
|
||||
var GitCommit = ""
|
||||
|
|
|
@ -1,25 +1,157 @@
|
|||
# IBC CLI Usage
|
||||
# IBC Doubble Hubble
|
||||
|
||||
## initialize
|
||||
## Remove remaining data
|
||||
|
||||
```bash
|
||||
basecoind init # copy the recover key
|
||||
basecli keys add keyname --recover
|
||||
basecoind start
|
||||
```console
|
||||
> rm -r ~/.chain1
|
||||
> rm -r ~/.chain2
|
||||
> rm -r ~/.basecli
|
||||
```
|
||||
|
||||
## transfer
|
||||
## Initialize both chains
|
||||
|
||||
`transfer` sends coins from one chain to another(or itself).
|
||||
```console
|
||||
> basecoind init --home ~/.chain1
|
||||
I[04-02|14:03:33.704] Generated private validator module=main path=/home/mossid/.chain1/config/priv_validator.json
|
||||
I[04-02|14:03:33.705] Generated genesis file module=main path=/home/mossid/.chain1/config/genesis.json
|
||||
{
|
||||
"secret": "crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon",
|
||||
"account": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"validator": {
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "8C9917D5E982E221F5A1450103102B44BBFC1E8768126C606246CB37B5794F4D"
|
||||
},
|
||||
"power": 10,
|
||||
"name": ""
|
||||
},
|
||||
"node_id": "3ac8e6242315fd62143dc3e52c161edaaa6b1a64",
|
||||
"chain_id": "test-chain-ZajMfr"
|
||||
}
|
||||
> ADDR1=C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
|
||||
> ID1=test-chain-ZajMfr
|
||||
> NODE1=tcp://0.0.0.0:36657
|
||||
> basecli keys add key1 --recover
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon
|
||||
key1 C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
|
||||
|
||||
|
||||
> basecoind init --home ~/.chain2
|
||||
I[04-02|14:09:14.453] Generated private validator module=main path=/home/mossid/.chain2/config/priv_validator.json
|
||||
I[04-02|14:09:14.453] Generated genesis file module=main path=/home/mossid/.chain2/config/genesis.json
|
||||
{
|
||||
"secret": "age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon",
|
||||
"account": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"validator": {
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "A94FE4B9AD763D301F4DD5A2766009812495FB7A79F1275FB8A5AF09B44FD5F3"
|
||||
},
|
||||
"power": 10,
|
||||
"name": ""
|
||||
},
|
||||
"node_id": "ad26831330e1c72b85276d53c20f0680e6fd4cf5"
|
||||
"chain_id": "test-chain-4XHTPn"
|
||||
}
|
||||
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
|
||||
> ID2=test-chain-4XHTPn
|
||||
> NODE2=tcp://0.0.0.0:46657
|
||||
> basecli keys add key2 --recover
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon
|
||||
key2 DC26002735D3AA9573707CFA6D77C12349E49868
|
||||
|
||||
|
||||
> basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656
|
||||
...
|
||||
|
||||
> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:46658 --rpc.laddr tcp://0.0.0.0:46657 --p2p.laddr tcp://0.0.0.0:46656
|
||||
...
|
||||
```
|
||||
## Check balance
|
||||
|
||||
```console
|
||||
> basecli account $ADDR1 --node $NODE1
|
||||
{
|
||||
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"sequence": 0,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
> basecli account $ADDR2 --node $NODE2
|
||||
{
|
||||
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"sequence": 0,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
```bash
|
||||
basecli transfer --name keyname --to address_of_destination --amount 10mycoin --chain test-chain-AAAAAA --chain-id AAAAAA
|
||||
```
|
||||
|
||||
The id of the chain can be found in `$HOME/.basecoind/config/genesis.json`
|
||||
## Transfer coins (addr1:chain1 -> addr2:chain2)
|
||||
|
||||
```console
|
||||
> basecli transfer --name key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1
|
||||
Password to sign with 'key1':
|
||||
Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0
|
||||
> basecli account $ADDR1 --node $NODE1
|
||||
{
|
||||
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740982
|
||||
}
|
||||
],
|
||||
"public_key": {
|
||||
"type": "ed25519",
|
||||
"data": "9828FF1780A066A0D93D840737566B697035448D6C880807322BED8919348B2B"
|
||||
},
|
||||
"sequence": 1,
|
||||
"name": ""
|
||||
}
|
||||
```
|
||||
|
||||
## Relay IBC packets
|
||||
|
||||
```console
|
||||
> basecli relay --name key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2
|
||||
Password to sign with 'key2':
|
||||
I[04-03|16:18:59.984] Detected IBC packet number=0
|
||||
I[04-03|16:19:00.869] Relayed IBC packet number=0
|
||||
> basecli account $ADDR2 --node $NODE2
|
||||
{
|
||||
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254741002
|
||||
}
|
||||
],
|
||||
"public_key": {
|
||||
"type": "ed25519",
|
||||
"data": "F52B4FA545F4E9BFE5D7AF1DD2236899FDEF905F9B3057C38D7C01BF1B8EB52E"
|
||||
},
|
||||
"sequence": 1,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
## relay
|
||||
|
||||
```bash
|
||||
basecli relay --name keyname --from-chain-id test-chain-AAAAAA --from-chain-node=tcp://0.0.0.0:46657 --to-chain-id test-chain-AAAAAA --to-chain-node=tcp://0.0.0.0:46657
|
||||
```
|
||||
|
|
|
@ -77,7 +77,7 @@ func buildMsg(from sdk.Address) (sdk.Msg, error) {
|
|||
}
|
||||
to := sdk.Address(bz)
|
||||
|
||||
packet := ibc.NewIBCPacket(from, to, coins, client.FlagChainID,
|
||||
packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(client.FlagChainID),
|
||||
viper.GetString(flagChain))
|
||||
|
||||
msg := ibc.IBCTransferMsg{
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
|
||||
|
@ -30,6 +32,8 @@ type relayCommander struct {
|
|||
decoder sdk.AccountDecoder
|
||||
mainStore string
|
||||
ibcStore string
|
||||
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
||||
|
@ -38,6 +42,8 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
|||
decoder: authcmd.GetAccountDecoder(cdc),
|
||||
ibcStore: "ibc",
|
||||
mainStore: "main",
|
||||
|
||||
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -86,27 +92,26 @@ func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode
|
|||
}
|
||||
|
||||
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
||||
|
||||
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var processed int64
|
||||
if processedbz == nil {
|
||||
processed = 0
|
||||
} else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
OUTER:
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var processed int64
|
||||
if processedbz == nil {
|
||||
processed = 0
|
||||
} else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lengthKey := ibc.EgressLengthKey(toChainID)
|
||||
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying outgoing packet list length: '%s'\n", err)
|
||||
c.logger.Error("Error querying outgoing packet list length", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
var egressLength int64
|
||||
|
@ -115,25 +120,30 @@ OUTER:
|
|||
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("egressLength queried: %d\n", egressLength)
|
||||
if egressLength > processed {
|
||||
c.logger.Info("Detected IBC packet", "number", egressLength-1)
|
||||
}
|
||||
|
||||
seq := c.getSequence(toChainNode)
|
||||
|
||||
for i := processed; i < egressLength; i++ {
|
||||
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying egress packet: '%s'\n", err)
|
||||
c.logger.Error("Error querying egress packet", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
viper.Set(client.FlagSequence, seq)
|
||||
seq++
|
||||
|
||||
err = c.broadcastTx(toChainNode, c.refine(egressbz, i, passphrase))
|
||||
if err != nil {
|
||||
fmt.Printf("Error broadcasting ingress packet: '%s'\n", err)
|
||||
c.logger.Error("Error broadcasting ingress packet", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
fmt.Printf("Relayed packet: %d\n", i)
|
||||
c.logger.Info("Relayed IBC packet", "number", i)
|
||||
}
|
||||
|
||||
processed = egressLength
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,8 +158,6 @@ func query(node string, key []byte, storeName string) (res []byte, err error) {
|
|||
func (c relayCommander) broadcastTx(node string, tx []byte) error {
|
||||
orig := viper.GetString(client.FlagNode)
|
||||
viper.Set(client.FlagNode, node)
|
||||
seq := c.getSequence(node) + 1
|
||||
viper.Set(client.FlagSequence, seq)
|
||||
_, err := builder.BroadcastTx(tx)
|
||||
viper.Set(client.FlagNode, orig)
|
||||
return err
|
||||
|
@ -160,6 +168,7 @@ func (c relayCommander) getSequence(node string) int64 {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
account, err := c.decoder(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -168,6 +177,10 @@ func (c relayCommander) getSequence(node string) int64 {
|
|||
return account.GetSequence()
|
||||
}
|
||||
|
||||
func setSequence(seq int64) {
|
||||
viper.Set(client.FlagSequence, seq)
|
||||
}
|
||||
|
||||
func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte {
|
||||
var packet ibc.IBCPacket
|
||||
if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil {
|
||||
|
|
|
@ -71,17 +71,6 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
|||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// XXX should be send in the msg (init in CLI)
|
||||
//func getSender() sdk.Address {
|
||||
//signers := msg.GetSigners()
|
||||
//if len(signers) != 1 {
|
||||
//return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
|
||||
//}
|
||||
//sender := signers[0]
|
||||
//}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
|
@ -187,8 +176,11 @@ func BondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candidat
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newShares := k.candidateAddTokens(ctx, candidate, amount.Amount)
|
||||
p := k.GetPool(ctx)
|
||||
p, candidate, newShares := p.candidateAddTokens(candidate, amount.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
k.setPool(ctx, p)
|
||||
k.setCandidate(ctx, candidate)
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
return nil
|
||||
}
|
||||
|
@ -258,7 +250,9 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
}
|
||||
|
||||
// Add the coins
|
||||
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
|
||||
p := k.GetPool(ctx)
|
||||
var returnAmount int64
|
||||
p, candidate, returnAmount = p.candidateRemoveShares(candidate, shares)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||
|
||||
|
@ -267,7 +261,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
|
||||
// change the share types to unbonded if they were not already
|
||||
if candidate.Status == Bonded {
|
||||
k.bondedToUnbondedPool(ctx, candidate)
|
||||
p, candidate = p.bondedToUnbondedPool(candidate)
|
||||
}
|
||||
|
||||
// lastly update the status
|
||||
|
@ -280,6 +274,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
} else {
|
||||
k.setCandidate(ctx, candidate)
|
||||
}
|
||||
k.setPool(ctx, p)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
|
@ -293,12 +288,16 @@ func UnbondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candid
|
|||
}
|
||||
bond.Shares = bond.Shares.Sub(shares)
|
||||
|
||||
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
|
||||
p := k.GetPool(ctx)
|
||||
var returnAmount int64
|
||||
p, candidate, returnAmount = p.candidateRemoveShares(candidate, shares)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
|
||||
_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.setPool(ctx, p)
|
||||
k.setCandidate(ctx, candidate)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,248 +1,245 @@
|
|||
package stake
|
||||
|
||||
//import (
|
||||
//"strconv"
|
||||
//"testing"
|
||||
/*
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
//"github.com/stretchr/testify/assert"
|
||||
//"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
//crypto "github.com/tendermint/go-crypto"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
//sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
//)
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
////______________________________________________________________________
|
||||
//______________________________________________________________________
|
||||
|
||||
//func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
||||
//return MsgDeclareCandidacy{
|
||||
//Description: Description{},
|
||||
//CandidateAddr: address,
|
||||
//Bond: sdk.Coin{"fermion", amt},
|
||||
//PubKey: pubKey,
|
||||
//}
|
||||
//}
|
||||
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
||||
return MsgDeclareCandidacy{
|
||||
Description: Description{},
|
||||
CandidateAddr: address,
|
||||
Bond: sdk.Coin{"fermion", amt},
|
||||
PubKey: pubKey,
|
||||
}
|
||||
}
|
||||
|
||||
//func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
|
||||
//return MsgDelegate{
|
||||
//DelegatorAddr: delegatorAddr,
|
||||
//CandidateAddr: candidateAddr,
|
||||
//Bond: sdk.Coin{"fermion", amt},
|
||||
//}
|
||||
//}
|
||||
func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
Bond: sdk.Coin{"fermion", amt},
|
||||
}
|
||||
}
|
||||
|
||||
//func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
||||
//ctxDeliver, _, keeper := createTestInput(t, addrs[0], false, 1000)
|
||||
//ctxCheck, _, keeper := createTestInput(t, addrs[0], true, 1000)
|
||||
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, addrs[0], false, 1000)
|
||||
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "%v", got)
|
||||
|
||||
//// one sender can bond to two different addresses
|
||||
//msgDeclareCandidacy.Address = addrs[1]
|
||||
//err := checker.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.Nil(t, err, "didn't expected error on checkTx")
|
||||
// one sender cannot bond twice
|
||||
msgDeclareCandidacy.PubKey = pks[1]
|
||||
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.False(t, got.IsOK(), "%v", got)
|
||||
}
|
||||
|
||||
//// two addrs cant bond to the same pubkey
|
||||
//checker.sender = addrs[1]
|
||||
//msgDeclareCandidacy.Address = addrs[0]
|
||||
//err = checker.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NotNil(t, err, "expected error on checkTx")
|
||||
//}
|
||||
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, addrs[0], false, 1000)
|
||||
|
||||
//func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
// first declare candidacy
|
||||
bondAmount := int64(10)
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], bondAmount)
|
||||
got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
assert.NoError(t, got, "expected declare candidacy msg to be ok, got %v", got)
|
||||
expectedBond := bondAmount // 1 since we send 1 at the start of loop,
|
||||
|
||||
//// first declare candidacy
|
||||
//bondAmount := int64(10)
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], bondAmount)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected declare candidacy msg to be ok, got %v", got)
|
||||
//expectedBond := bondAmount // 1 since we send 1 at the start of loop,
|
||||
// just send the same msgbond multiple times
|
||||
msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
|
||||
for i := 0; i < 5; i++ {
|
||||
got := deliverer.delegate(msgDelegate)
|
||||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// just send the same msgbond multiple times
|
||||
//msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
|
||||
//for i := 0; i < 5; i++ {
|
||||
//got := deliverer.delegate(msgDelegate)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidates := mapper.GetCandidates()
|
||||
expectedBond += bondAmount
|
||||
//expectedSender := initSender - expectedBond
|
||||
gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
//gotSender := accStore[string(deliverer.sender)] //XXX use StoreMapper
|
||||
assert.Equal(t, expectedBond, gotBonded, "i: %v, %v, %v", i, expectedBond, gotBonded)
|
||||
//assert.Equal(t, expectedSender, gotSender, "i: %v, %v, %v", i, expectedSender, gotSender) // XXX fix
|
||||
}
|
||||
}
|
||||
|
||||
////Check that the accounts and the bond account have the appropriate values
|
||||
//candidates := mapper.GetCandidates()
|
||||
//expectedBond += bondAmount
|
||||
////expectedSender := initSender - expectedBond
|
||||
//gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
////gotSender := accStore[string(deliverer.sender)] //XXX use StoreMapper
|
||||
//assert.Equal(t, expectedBond, gotBonded, "i: %v, %v, %v", i, expectedBond, gotBonded)
|
||||
////assert.Equal(t, expectedSender, gotSender, "i: %v, %v, %v", i, expectedSender, gotSender) // XXX fix
|
||||
//}
|
||||
//}
|
||||
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, addrs[0], false, 0)
|
||||
|
||||
//func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 0)
|
||||
// set initial bond
|
||||
initBond := int64(1000)
|
||||
//accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
|
||||
got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
|
||||
assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
|
||||
|
||||
//// set initial bond
|
||||
//initBond := int64(1000)
|
||||
////accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
|
||||
//got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
|
||||
//assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
|
||||
// just send the same msgunbond multiple times
|
||||
// XXX use decimals here
|
||||
unbondShares, unbondSharesStr := int64(10), "10"
|
||||
msgUndelegate := NewMsgUnbond(addrs[0], unbondSharesStr)
|
||||
nUnbonds := 5
|
||||
for i := 0; i < nUnbonds; i++ {
|
||||
got := deliverer.unbond(msgUndelegate)
|
||||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// just send the same msgunbond multiple times
|
||||
//// XXX use decimals here
|
||||
//unbondShares, unbondSharesStr := int64(10), "10"
|
||||
//msgUndelegate := NewMsgUnbond(addrs[0], unbondSharesStr)
|
||||
//nUnbonds := 5
|
||||
//for i := 0; i < nUnbonds; i++ {
|
||||
//got := deliverer.unbond(msgUndelegate)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidates := mapper.GetCandidates()
|
||||
expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
|
||||
//expectedSender := initSender + (initBond - expectedBond)
|
||||
gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
//gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
|
||||
|
||||
////Check that the accounts and the bond account have the appropriate values
|
||||
//candidates := mapper.GetCandidates()
|
||||
//expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
|
||||
////expectedSender := initSender + (initBond - expectedBond)
|
||||
//gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
////gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
|
||||
assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
|
||||
//assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
|
||||
}
|
||||
|
||||
//assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
|
||||
////assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
|
||||
//}
|
||||
// these are more than we have bonded now
|
||||
errorCases := []int64{
|
||||
//1<<64 - 1, // more than int64
|
||||
//1<<63 + 1, // more than int64
|
||||
1<<63 - 1,
|
||||
1 << 31,
|
||||
initBond,
|
||||
}
|
||||
for _, c := range errorCases {
|
||||
unbondShares := strconv.Itoa(int(c))
|
||||
msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
|
||||
got = deliverer.unbond(msgUndelegate)
|
||||
assert.Error(t, got, "expected unbond msg to fail")
|
||||
}
|
||||
|
||||
//// these are more than we have bonded now
|
||||
//errorCases := []int64{
|
||||
////1<<64 - 1, // more than int64
|
||||
////1<<63 + 1, // more than int64
|
||||
//1<<63 - 1,
|
||||
//1 << 31,
|
||||
//initBond,
|
||||
//}
|
||||
//for _, c := range errorCases {
|
||||
//unbondShares := strconv.Itoa(int(c))
|
||||
//msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.Error(t, got, "expected unbond msg to fail")
|
||||
//}
|
||||
leftBonded := initBond - unbondShares*int64(nUnbonds)
|
||||
|
||||
//leftBonded := initBond - unbondShares*int64(nUnbonds)
|
||||
// should be unable to unbond one more than we have
|
||||
msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
|
||||
got = deliverer.unbond(msgUndelegate)
|
||||
assert.Error(t, got, "expected unbond msg to fail")
|
||||
|
||||
//// should be unable to unbond one more than we have
|
||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.Error(t, got, "expected unbond msg to fail")
|
||||
// should be able to unbond just what we have
|
||||
msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
|
||||
got = deliverer.unbond(msgUndelegate)
|
||||
assert.NoError(t, got, "expected unbond msg to pass")
|
||||
}
|
||||
|
||||
//// should be able to unbond just what we have
|
||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.NoError(t, got, "expected unbond msg to pass")
|
||||
//}
|
||||
func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
||||
initSender := int64(1000)
|
||||
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
|
||||
ctx, mapper, keeper := createTestInput(t, addrs[0], false, initSender)
|
||||
addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
|
||||
//func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
||||
//initSender := int64(1000)
|
||||
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
|
||||
//addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
// bond them all
|
||||
for i, addr := range addrs {
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
|
||||
deliverer.sender = addr
|
||||
got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// bond them all
|
||||
//for i, addr := range addrs {
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
|
||||
//deliverer.sender = addr
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the account is bonded
|
||||
candidates := mapper.GetCandidates()
|
||||
require.Equal(t, i, len(candidates))
|
||||
val := candidates[i]
|
||||
balanceExpd := initSender - 10
|
||||
balanceGot := accStore.GetAccount(ctx, val.Address).GetCoins()
|
||||
assert.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
|
||||
assert.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
|
||||
assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
}
|
||||
|
||||
////Check that the account is bonded
|
||||
//candidates := mapper.GetCandidates()
|
||||
//require.Equal(t, i, len(candidates))
|
||||
//val := candidates[i]
|
||||
//balanceExpd := initSender - 10
|
||||
//balanceGot := accStore.GetAccount(ctx, val.Address).GetCoins()
|
||||
//assert.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
|
||||
//assert.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
|
||||
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
//}
|
||||
// unbond them all
|
||||
for i, addr := range addrs {
|
||||
candidatePre := mapper.GetCandidate(addrs[i])
|
||||
msgUndelegate := NewMsgUnbond(addrs[i], "10")
|
||||
deliverer.sender = addr
|
||||
got := deliverer.unbond(msgUndelegate)
|
||||
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// unbond them all
|
||||
//for i, addr := range addrs {
|
||||
//candidatePre := mapper.GetCandidate(addrs[i])
|
||||
//msgUndelegate := NewMsgUnbond(addrs[i], "10")
|
||||
//deliverer.sender = addr
|
||||
//got := deliverer.unbond(msgUndelegate)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the account is unbonded
|
||||
candidates := mapper.GetCandidates()
|
||||
assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
|
||||
|
||||
////Check that the account is unbonded
|
||||
//candidates := mapper.GetCandidates()
|
||||
//assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
|
||||
candidatePost := mapper.GetCandidate(addrs[i])
|
||||
balanceExpd := initSender
|
||||
balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
|
||||
assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
|
||||
assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
}
|
||||
}
|
||||
|
||||
//candidatePost := mapper.GetCandidate(addrs[i])
|
||||
//balanceExpd := initSender
|
||||
//balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
|
||||
//assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
|
||||
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
//}
|
||||
//}
|
||||
func TestMultipleMsgDelegate(t *testing.T) {
|
||||
sender, delegators := addrs[0], addrs[1:]
|
||||
_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
ctx, _, keeper := createTestInput(t, addrs[0], false, 0)
|
||||
|
||||
//func TestMultipleMsgDelegate(t *testing.T) {
|
||||
//sender, delegators := addrs[0], addrs[1:]
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
//first make a candidate
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
|
||||
got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
require.NoError(t, got, "expected msg to be ok, got %v", got)
|
||||
|
||||
////first make a candidate
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//require.NoError(t, got, "expected msg to be ok, got %v", got)
|
||||
// delegate multiple parties
|
||||
for i, delegator := range delegators {
|
||||
msgDelegate := newTestMsgDelegate(10, sender)
|
||||
deliverer.sender = delegator
|
||||
got := deliverer.delegate(msgDelegate)
|
||||
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// delegate multiple parties
|
||||
//for i, delegator := range delegators {
|
||||
//msgDelegate := newTestMsgDelegate(10, sender)
|
||||
//deliverer.sender = delegator
|
||||
//got := deliverer.delegate(msgDelegate)
|
||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the account is bonded
|
||||
bond := mapper.getDelegatorBond(delegator, sender)
|
||||
assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||
}
|
||||
|
||||
////Check that the account is bonded
|
||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
||||
//assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||
//}
|
||||
// unbond them all
|
||||
for i, delegator := range delegators {
|
||||
msgUndelegate := NewMsgUnbond(sender, "10")
|
||||
deliverer.sender = delegator
|
||||
got := deliverer.unbond(msgUndelegate)
|
||||
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//// unbond them all
|
||||
//for i, delegator := range delegators {
|
||||
//msgUndelegate := NewMsgUnbond(sender, "10")
|
||||
//deliverer.sender = delegator
|
||||
//got := deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
//Check that the account is unbonded
|
||||
bond := mapper.getDelegatorBond(delegator, sender)
|
||||
assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
|
||||
}
|
||||
}
|
||||
|
||||
////Check that the account is unbonded
|
||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
||||
//assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
|
||||
//}
|
||||
//}
|
||||
func TestVoidCandidacy(t *testing.T) {
|
||||
sender, delegator := addrs[0], addrs[1]
|
||||
_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
|
||||
//func TestVoidCandidacy(t *testing.T) {
|
||||
//sender, delegator := addrs[0], addrs[1]
|
||||
//_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
// create the candidate
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
||||
got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
//// create the candidate
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
// bond a delegator
|
||||
msgDelegate := newTestMsgDelegate(10, addrs[0])
|
||||
deliverer.sender = delegator
|
||||
got = deliverer.delegate(msgDelegate)
|
||||
require.NoError(t, got, "expected ok, got %v", got)
|
||||
|
||||
//// bond a delegator
|
||||
//msgDelegate := newTestMsgDelegate(10, addrs[0])
|
||||
//deliverer.sender = delegator
|
||||
//got = deliverer.delegate(msgDelegate)
|
||||
//require.NoError(t, got, "expected ok, got %v", got)
|
||||
// unbond the candidates bond portion
|
||||
msgUndelegate := NewMsgUnbond(addrs[0], "10")
|
||||
deliverer.sender = sender
|
||||
got = deliverer.unbond(msgUndelegate)
|
||||
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
//// unbond the candidates bond portion
|
||||
//msgUndelegate := NewMsgUnbond(addrs[0], "10")
|
||||
//deliverer.sender = sender
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
// test that this pubkey cannot yet be bonded too
|
||||
deliverer.sender = delegator
|
||||
got = deliverer.delegate(msgDelegate)
|
||||
assert.Error(t, got, "expected error, got %v", got)
|
||||
|
||||
//// test that this pubkey cannot yet be bonded too
|
||||
//deliverer.sender = delegator
|
||||
//got = deliverer.delegate(msgDelegate)
|
||||
//assert.Error(t, got, "expected error, got %v", got)
|
||||
// test that the delegator can still withdraw their bonds
|
||||
got = deliverer.unbond(msgUndelegate)
|
||||
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
//// test that the delegator can still withdraw their bonds
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
//// verify that the pubkey can now be reused
|
||||
//got = deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected ok, got %v", got)
|
||||
//}
|
||||
// verify that the pubkey can now be reused
|
||||
got = deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
assert.NoError(t, got, "expected ok, got %v", got)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
@ -87,6 +89,11 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
// if the voting power is the same no need to update any of the other indexes
|
||||
if oldFound && oldCandidate.Assets.Equal(candidate.Assets) {
|
||||
return
|
||||
}
|
||||
|
||||
// update the list ordered by voting power
|
||||
if oldFound {
|
||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
||||
|
@ -94,11 +101,19 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
|
|||
store.Set(GetValidatorKey(address, validator.VotingPower, k.cdc), bz)
|
||||
|
||||
// add to the validators to update list if is already a validator
|
||||
if store.Get(GetRecentValidatorKey(address)) == nil {
|
||||
return
|
||||
}
|
||||
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
|
||||
// or is a new validator
|
||||
setAcc := false
|
||||
if store.Get(GetRecentValidatorKey(address)) != nil {
|
||||
setAcc = true
|
||||
|
||||
// want to check in the else statement because inefficient
|
||||
} else if k.isNewValidator(ctx, store, address) {
|
||||
setAcc = true
|
||||
}
|
||||
if setAcc {
|
||||
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
||||
|
@ -112,6 +127,7 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
|||
// delete the old candidate record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(GetCandidateKey(address))
|
||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
||||
|
||||
// delete from recent and power weighted validator groups if the validator
|
||||
// exists and add validator with zero power to the validator updates
|
||||
|
@ -124,7 +140,6 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
|||
}
|
||||
store.Set(GetAccUpdateValidatorKey(address), bz)
|
||||
store.Delete(GetRecentValidatorKey(address))
|
||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
@ -136,12 +151,18 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
|||
func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// clear the recent validators store
|
||||
k.deleteSubSpace(store, RecentValidatorsKey)
|
||||
// clear the recent validators store, add to the ToKickOut Temp store
|
||||
iterator := store.Iterator(subspace(RecentValidatorsKey))
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
addr := AddrFromKey(iterator.Key())
|
||||
store.Set(GetToKickOutValidatorKey(addr), []byte{})
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxVal := k.GetParams(ctx).MaxValidators
|
||||
iterator := store.ReverseIterator(subspace(ValidatorsKey)) //smallest to largest
|
||||
iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
|
||||
validators = make([]Validator, maxVal)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
|
@ -157,15 +178,58 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
|||
}
|
||||
validators[i] = val
|
||||
|
||||
// remove from ToKickOut group
|
||||
store.Delete(GetToKickOutValidatorKey(val.Address))
|
||||
|
||||
// also add to the recent validators group
|
||||
store.Set(GetRecentValidatorKey(val.Address), bz)
|
||||
store.Set(GetRecentValidatorKey(val.Address), bz) // XXX should store nothing
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
// add any kicked out validators to the acc change
|
||||
iterator = store.Iterator(subspace(ToKickOutValidatorsKey))
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
addr := AddrFromKey(iterator.Key())
|
||||
bz, err := k.cdc.MarshalBinary(Validator{addr, sdk.ZeroRat})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GetAccUpdateValidatorKey(addr), bz)
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// TODO this is madly inefficient because need to call every time we set a candidate
|
||||
// Should use something better than an iterator maybe?
|
||||
// Used to determine if something has just been added to the actual validator set
|
||||
func (k Keeper) isNewValidator(ctx sdk.Context, store sdk.KVStore, address sdk.Address) bool {
|
||||
// add the actual validator power sorted store
|
||||
maxVal := k.GetParams(ctx).MaxValidators
|
||||
iterator := store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
|
||||
for i := 0; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxVal-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var val Validator
|
||||
err := k.cdc.UnmarshalBinary(bz, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if bytes.Equal(val.Address, address) {
|
||||
return true
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Is the address provided a part of the most recently saved validator group?
|
||||
func (k Keeper) IsRecentValidator(ctx sdk.Context, address sdk.Address) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
@ -298,3 +362,33 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
|
|||
store.Set(ParamKey, b)
|
||||
k.params = Params{} // clear the cache
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the pool
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
|
||||
// check if cached before anything
|
||||
if k.gs != (Pool{}) {
|
||||
return k.gs
|
||||
}
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
return initialPool()
|
||||
}
|
||||
err := k.cdc.UnmarshalBinary(b, &gs)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b, err := k.cdc.MarshalBinary(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(PoolKey, b)
|
||||
k.gs = Pool{} // clear the cache
|
||||
}
|
||||
|
|
|
@ -15,9 +15,11 @@ var (
|
|||
CandidatesKey = []byte{0x02} // prefix for each key to a candidate
|
||||
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
|
||||
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
|
||||
RecentValidatorsKey = []byte{0x04} // prefix for each key to the last updated validator group
|
||||
RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
|
||||
|
||||
DelegatorBondKeyPrefix = []byte{0x05} // prefix for each key to a delegator's bond
|
||||
ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group
|
||||
|
||||
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
@ -43,6 +45,16 @@ func GetRecentValidatorKey(addr sdk.Address) []byte {
|
|||
return append(RecentValidatorsKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// reverse operation of GetRecentValidatorKey
|
||||
func AddrFromKey(key []byte) sdk.Address {
|
||||
return key[1:]
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetToKickOutValidatorKey(addr sdk.Address) []byte {
|
||||
return append(ToKickOutValidatorsKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for delegator bond with candidate
|
||||
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
|
||||
|
|
|
@ -5,39 +5,22 @@ import (
|
|||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
addrDel1 = addrs[0]
|
||||
addrDel2 = addrs[1]
|
||||
addrVal1 = addrs[2]
|
||||
addrVal2 = addrs[3]
|
||||
addrVal3 = addrs[4]
|
||||
pk1 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk2 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk3 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
|
||||
candidate1 = Candidate{
|
||||
Address: addrVal1,
|
||||
PubKey: pk1,
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
addrDels = []sdk.Address{
|
||||
addrs[0],
|
||||
addrs[1],
|
||||
}
|
||||
candidate2 = Candidate{
|
||||
Address: addrVal2,
|
||||
PubKey: pk2,
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
candidate3 = Candidate{
|
||||
Address: addrVal3,
|
||||
PubKey: pk3,
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
addrVals = []sdk.Address{
|
||||
addrs[2],
|
||||
addrs[3],
|
||||
addrs[4],
|
||||
addrs[5],
|
||||
addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -45,6 +28,18 @@ var (
|
|||
func TestCandidate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
//construct the candidates
|
||||
var candidates [3]Candidate
|
||||
amts := []int64{9, 8, 7}
|
||||
for i, amt := range amts {
|
||||
candidates[i] = Candidate{
|
||||
Address: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
candidatesEqual := func(c1, c2 Candidate) bool {
|
||||
return c1.Status == c2.Status &&
|
||||
c1.PubKey.Equals(c2.PubKey) &&
|
||||
|
@ -55,47 +50,47 @@ func TestCandidate(t *testing.T) {
|
|||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetCandidate(ctx, addrVal1)
|
||||
_, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resCands := keeper.GetCandidates(ctx, 100)
|
||||
assert.Zero(t, len(resCands))
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
resCand, found := keeper.GetCandidate(ctx, addrVal1)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
resCand, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(t, candidatesEqual(candidate1, resCand), "%v \n %v", resCand, candidate1)
|
||||
assert.True(t, candidatesEqual(candidates[0], resCand), "%v \n %v", resCand, candidates[0])
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
candidate1.Liabilities = sdk.NewRat(99)
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal1)
|
||||
candidates[0].Liabilities = sdk.NewRat(99)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(t, candidatesEqual(candidate1, resCand))
|
||||
assert.True(t, candidatesEqual(candidates[0], resCand))
|
||||
|
||||
// also test that the address has been added to address list
|
||||
resCands = keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, 1, len(resCands))
|
||||
assert.Equal(t, addrVal1, resCands[0].Address)
|
||||
assert.Equal(t, addrVals[0], resCands[0].Address)
|
||||
|
||||
// add other candidates
|
||||
keeper.setCandidate(ctx, candidate2)
|
||||
keeper.setCandidate(ctx, candidate3)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal2)
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(t, candidatesEqual(candidate2, resCand), "%v \n %v", resCand, candidate2)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal3)
|
||||
assert.True(t, candidatesEqual(candidates[1], resCand), "%v \n %v", resCand, candidates[1])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[2])
|
||||
require.True(t, found)
|
||||
assert.True(t, candidatesEqual(candidate3, resCand), "%v \n %v", resCand, candidate3)
|
||||
assert.True(t, candidatesEqual(candidates[2], resCand), "%v \n %v", resCand, candidates[2])
|
||||
resCands = keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, 3, len(resCands))
|
||||
assert.True(t, candidatesEqual(candidate1, resCands[0]), "%v \n %v", resCands[0], candidate1)
|
||||
assert.True(t, candidatesEqual(candidate2, resCands[1]), "%v \n %v", resCands[1], candidate2)
|
||||
assert.True(t, candidatesEqual(candidate3, resCands[2]), "%v \n %v", resCands[2], candidate3)
|
||||
assert.True(t, candidatesEqual(candidates[0], resCands[0]), "%v \n %v", resCands[0], candidates[0])
|
||||
assert.True(t, candidatesEqual(candidates[1], resCands[1]), "%v \n %v", resCands[1], candidates[1])
|
||||
assert.True(t, candidatesEqual(candidates[2], resCands[2]), "%v \n %v", resCands[2], candidates[2])
|
||||
|
||||
// remove a record
|
||||
keeper.removeCandidate(ctx, candidate2.Address)
|
||||
_, found = keeper.GetCandidate(ctx, addrVal2)
|
||||
keeper.removeCandidate(ctx, candidates[1].Address)
|
||||
_, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
|
@ -103,12 +98,24 @@ func TestCandidate(t *testing.T) {
|
|||
func TestBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
// first add a candidate1 to delegate too
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
//construct the candidates
|
||||
amts := []int64{9, 8, 7}
|
||||
var candidates [3]Candidate
|
||||
for i, amt := range amts {
|
||||
candidates[i] = Candidate{
|
||||
Address: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// first add a candidates[0] to delegate too
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
|
||||
bond1to1 := DelegatorBond{
|
||||
DelegatorAddr: addrDel1,
|
||||
CandidateAddr: addrVal1,
|
||||
DelegatorAddr: addrDels[0],
|
||||
CandidateAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
|
@ -119,30 +126,30 @@ func TestBond(t *testing.T) {
|
|||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
||||
_, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
||||
resBond, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found = keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
||||
resBond, found = keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// add some more records
|
||||
keeper.setCandidate(ctx, candidate2)
|
||||
keeper.setCandidate(ctx, candidate3)
|
||||
bond1to2 := DelegatorBond{addrDel1, addrVal2, sdk.NewRat(9)}
|
||||
bond1to3 := DelegatorBond{addrDel1, addrVal3, sdk.NewRat(9)}
|
||||
bond2to1 := DelegatorBond{addrDel2, addrVal1, sdk.NewRat(9)}
|
||||
bond2to2 := DelegatorBond{addrDel2, addrVal2, sdk.NewRat(9)}
|
||||
bond2to3 := DelegatorBond{addrDel2, addrVal3, sdk.NewRat(9)}
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9)}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9)}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9)}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9)}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9)}
|
||||
keeper.setDelegatorBond(ctx, bond1to2)
|
||||
keeper.setDelegatorBond(ctx, bond1to3)
|
||||
keeper.setDelegatorBond(ctx, bond2to1)
|
||||
|
@ -150,16 +157,16 @@ func TestBond(t *testing.T) {
|
|||
keeper.setDelegatorBond(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := keeper.getDelegatorBonds(ctx, addrDel1, 5)
|
||||
resBonds := keeper.getDelegatorBonds(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
|
||||
assert.True(t, bondsEqual(bond1to3, resBonds[2]))
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 3)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 2)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||
|
@ -167,9 +174,9 @@ func TestBond(t *testing.T) {
|
|||
|
||||
// delete a record
|
||||
keeper.removeDelegatorBond(ctx, bond2to3)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal3)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[2])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||
|
@ -177,11 +184,11 @@ func TestBond(t *testing.T) {
|
|||
// delete all the records from delegator 2
|
||||
keeper.removeDelegatorBond(ctx, bond2to1)
|
||||
keeper.removeDelegatorBond(ctx, bond2to2)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal1)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[0])
|
||||
assert.False(t, found)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal2)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[1])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 0, len(resBonds))
|
||||
}
|
||||
|
||||
|
@ -193,14 +200,14 @@ func TestGetValidators(t *testing.T) {
|
|||
// initialize some candidates into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
candidates := make([]Candidate, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var candidates [5]Candidate
|
||||
for i, amt := range amts {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(amts[i]),
|
||||
Liabilities: sdk.NewRat(amts[i]),
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
keeper.setCandidate(ctx, c)
|
||||
candidates[i] = c
|
||||
|
@ -259,36 +266,282 @@ func TestGetValidators(t *testing.T) {
|
|||
assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// test the mechanism which keeps track of a validator set change
|
||||
func TestGetAccUpdateValidators(t *testing.T) {
|
||||
//TODO
|
||||
// test from nothing to something
|
||||
// test from something to nothing
|
||||
// test identical
|
||||
// test single value change
|
||||
// test multiple value change
|
||||
// test validator added at the beginning
|
||||
// test validator added in the middle
|
||||
// test validator added at the end
|
||||
// test multiple validators removed
|
||||
}
|
||||
|
||||
// clear the tracked changes to the validator set
|
||||
func TestClearAccUpdateValidators(t *testing.T) {
|
||||
//TODO
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
amts := []int64{100, 400, 200}
|
||||
candidates := make([]Candidate, len(amts))
|
||||
for i, amt := range amts {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
candidates[i] = c
|
||||
keeper.setCandidate(ctx, c)
|
||||
}
|
||||
|
||||
acc := keeper.getAccUpdateValidators(ctx)
|
||||
assert.Equal(t, len(amts), len(acc))
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 0, len(acc))
|
||||
}
|
||||
|
||||
// test the mechanism which keeps track of a validator set change
|
||||
func TestGetAccUpdateValidators(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
params := defaultParams()
|
||||
params.MaxValidators = 4
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// TODO eliminate use of candidatesIn here
|
||||
// tests could be clearer if they just
|
||||
// created the candidate at time of use
|
||||
// and were labelled by power in the comments
|
||||
// outlining in each test
|
||||
amts := []int64{10, 11, 12, 13, 1}
|
||||
var candidatesIn [5]Candidate
|
||||
for i, amt := range amts {
|
||||
candidatesIn[i] = Candidate{
|
||||
Address: addrs[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// test from nothing to something
|
||||
// candidate set: {} -> {c1, c3}
|
||||
// validator set: {} -> {c1, c3}
|
||||
// accUpdate set: {} -> {c1, c3}
|
||||
assert.Equal(t, 0, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.GetValidators(ctx)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.setCandidate(ctx, candidatesIn[1])
|
||||
keeper.setCandidate(ctx, candidatesIn[3])
|
||||
|
||||
vals := keeper.GetValidators(ctx) // to init recent validator set
|
||||
require.Equal(t, 2, len(vals))
|
||||
acc := keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 2, len(acc))
|
||||
candidates := keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 2, len(candidates))
|
||||
assert.Equal(t, candidates[0].validator(), acc[0])
|
||||
assert.Equal(t, candidates[1].validator(), acc[1])
|
||||
assert.Equal(t, candidates[0].validator(), vals[1])
|
||||
assert.Equal(t, candidates[1].validator(), vals[0])
|
||||
|
||||
// test identical,
|
||||
// candidate set: {c1, c3} -> {c1, c3}
|
||||
// accUpdate set: {} -> {}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
|
||||
require.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
// test single value change
|
||||
// candidate set: {c1, c3} -> {c1', c3}
|
||||
// accUpdate set: {} -> {c1'}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
candidates[0].Assets = sdk.NewRat(600)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 2, len(candidates))
|
||||
assert.True(t, candidates[0].Assets.Equal(sdk.NewRat(600)))
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 1, len(acc))
|
||||
assert.Equal(t, candidates[0].validator(), acc[0])
|
||||
|
||||
// test multiple value change
|
||||
// candidate set: {c1, c3} -> {c1', c3'}
|
||||
// accUpdate set: {c1, c3} -> {c1', c3'}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
candidates[0].Assets = sdk.NewRat(200)
|
||||
candidates[1].Assets = sdk.NewRat(100)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 2, len(acc))
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 2, len(candidates))
|
||||
require.Equal(t, candidates[0].validator(), acc[0])
|
||||
require.Equal(t, candidates[1].validator(), acc[1])
|
||||
|
||||
// test validtor added at the beginning
|
||||
// candidate set: {c1, c3} -> {c0, c1, c3}
|
||||
// accUpdate set: {} -> {c0}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.setCandidate(ctx, candidatesIn[0])
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 1, len(acc))
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 3, len(candidates))
|
||||
assert.Equal(t, candidates[0].validator(), acc[0])
|
||||
|
||||
// test validator added at the middle
|
||||
// candidate set: {c0, c1, c3} -> {c0, c1, c2, c3]
|
||||
// accUpdate set: {} -> {c2}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 3, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.setCandidate(ctx, candidatesIn[2])
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 1, len(acc))
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 4, len(candidates))
|
||||
assert.Equal(t, candidates[2].validator(), acc[0])
|
||||
|
||||
// test candidate added at the end but not inserted in the valset
|
||||
// candidate set: {c0, c1, c2, c3} -> {c0, c1, c2, c3, c4}
|
||||
// validator set: {c0, c1, c2, c3} -> {c0, c1, c2, c3}
|
||||
// accUpdate set: {} -> {}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 4, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.setCandidate(ctx, candidatesIn[4])
|
||||
|
||||
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
require.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx))) // max validator number is 4
|
||||
|
||||
// test candidate change its power but still not in the valset
|
||||
// candidate set: {c0, c1, c2, c3, c4} -> {c0, c1, c2, c3, c4}
|
||||
// validator set: {c0, c1, c2, c3} -> {c0, c1, c2, c3}
|
||||
// accUpdate set: {} -> {}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
candidatesIn[4].Assets = sdk.NewRat(1)
|
||||
keeper.setCandidate(ctx, candidatesIn[4])
|
||||
|
||||
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
require.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx))) // max validator number is 4
|
||||
|
||||
// test candidate change its power and become a validator (pushing out an existing)
|
||||
// candidate set: {c0, c1, c2, c3, c4} -> {c0, c1, c2, c3, c4}
|
||||
// validator set: {c0, c1, c2, c3} -> {c1, c2, c3, c4}
|
||||
// accUpdate set: {} -> {c0, c4}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
candidatesIn[4].Assets = sdk.NewRat(1000)
|
||||
keeper.setCandidate(ctx, candidatesIn[4])
|
||||
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 5, len(candidates))
|
||||
vals = keeper.GetValidators(ctx)
|
||||
require.Equal(t, 4, len(vals))
|
||||
assert.Equal(t, candidatesIn[1].Address, vals[1].Address)
|
||||
assert.Equal(t, candidatesIn[2].Address, vals[3].Address)
|
||||
assert.Equal(t, candidatesIn[3].Address, vals[2].Address)
|
||||
assert.Equal(t, candidatesIn[4].Address, vals[0].Address)
|
||||
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 2, len(acc), "%v", acc)
|
||||
|
||||
assert.Equal(t, candidatesIn[0].Address, acc[0].Address)
|
||||
assert.Equal(t, int64(0), acc[0].VotingPower.Evaluate())
|
||||
assert.Equal(t, vals[0], acc[1])
|
||||
|
||||
// test from something to nothing
|
||||
// candidate set: {c0, c1, c2, c3, c4} -> {}
|
||||
// validator set: {c1, c2, c3, c4} -> {}
|
||||
// accUpdate set: {} -> {c1, c2, c3, c4}
|
||||
keeper.clearAccUpdateValidators(ctx)
|
||||
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||
|
||||
keeper.removeCandidate(ctx, candidatesIn[0].Address)
|
||||
keeper.removeCandidate(ctx, candidatesIn[1].Address)
|
||||
keeper.removeCandidate(ctx, candidatesIn[2].Address)
|
||||
keeper.removeCandidate(ctx, candidatesIn[3].Address)
|
||||
keeper.removeCandidate(ctx, candidatesIn[4].Address)
|
||||
|
||||
vals = keeper.GetValidators(ctx)
|
||||
assert.Equal(t, 0, len(vals), "%v", vals)
|
||||
candidates = keeper.GetCandidates(ctx, 5)
|
||||
require.Equal(t, 0, len(candidates))
|
||||
acc = keeper.getAccUpdateValidators(ctx)
|
||||
require.Equal(t, 4, len(acc))
|
||||
assert.Equal(t, candidatesIn[1].Address, acc[0].Address)
|
||||
assert.Equal(t, candidatesIn[2].Address, acc[1].Address)
|
||||
assert.Equal(t, candidatesIn[3].Address, acc[2].Address)
|
||||
assert.Equal(t, candidatesIn[4].Address, acc[3].Address)
|
||||
assert.Equal(t, int64(0), acc[0].VotingPower.Evaluate())
|
||||
assert.Equal(t, int64(0), acc[1].VotingPower.Evaluate())
|
||||
assert.Equal(t, int64(0), acc[2].VotingPower.Evaluate())
|
||||
assert.Equal(t, int64(0), acc[3].VotingPower.Evaluate())
|
||||
}
|
||||
|
||||
// test if is a validator from the last update
|
||||
func TestIsRecentValidator(t *testing.T) {
|
||||
//TODO
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
amts := []int64{9, 8, 7, 10, 6}
|
||||
var candidatesIn [5]Candidate
|
||||
for i, amt := range amts {
|
||||
candidatesIn[i] = Candidate{
|
||||
Address: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// test that an empty validator set doesn't have any validators
|
||||
validators := keeper.GetValidators(ctx)
|
||||
assert.Equal(t, 0, len(validators))
|
||||
|
||||
// get the validators for the first time
|
||||
keeper.setCandidate(ctx, candidatesIn[0])
|
||||
keeper.setCandidate(ctx, candidatesIn[1])
|
||||
validators = keeper.GetValidators(ctx)
|
||||
require.Equal(t, 2, len(validators))
|
||||
assert.Equal(t, candidatesIn[0].validator(), validators[0])
|
||||
assert.Equal(t, candidatesIn[1].validator(), validators[1])
|
||||
|
||||
// test a basic retrieve of something that should be a recent validator
|
||||
assert.True(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address))
|
||||
assert.True(t, keeper.IsRecentValidator(ctx, candidatesIn[1].Address))
|
||||
|
||||
// test a basic retrieve of something that should not be a recent validator
|
||||
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[2].Address))
|
||||
|
||||
// remove that validator, but don't retrieve the recent validator group
|
||||
keeper.removeCandidate(ctx, candidatesIn[0].Address)
|
||||
|
||||
// test that removed validator is not considered a recent validator
|
||||
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address))
|
||||
}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
|
@ -305,3 +558,18 @@ func TestParams(t *testing.T) {
|
|||
resParams = keeper.GetParams(ctx)
|
||||
assert.Equal(t, expParams, resParams)
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
expPool := initialPool()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.TotalSupply = 777
|
||||
keeper.setPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
}
|
||||
|
|
107
x/stake/pool.go
107
x/stake/pool.go
|
@ -4,132 +4,115 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// load/save the global staking state
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
|
||||
// check if cached before anything
|
||||
if k.gs != (Pool{}) {
|
||||
return k.gs
|
||||
// get the bond ratio of the global state
|
||||
func (p Pool) bondedRatio() sdk.Rat {
|
||||
if p.TotalSupply > 0 {
|
||||
return sdk.NewRat(p.BondedPool, p.TotalSupply)
|
||||
}
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
return initialPool()
|
||||
}
|
||||
err := k.cdc.UnmarshalBinary(b, &gs)
|
||||
if err != nil {
|
||||
panic(err) // This error should never occur big problem if does
|
||||
}
|
||||
return
|
||||
return sdk.ZeroRat
|
||||
}
|
||||
|
||||
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b, err := k.cdc.MarshalBinary(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// get the exchange rate of bonded token per issued share
|
||||
func (p Pool) bondedShareExRate() sdk.Rat {
|
||||
if p.BondedShares.IsZero() {
|
||||
return sdk.OneRat
|
||||
}
|
||||
store.Set(PoolKey, b)
|
||||
k.gs = Pool{} // clear the cache
|
||||
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
//TODO make these next two functions more efficient should be reading and writting to state ye know
|
||||
// get the exchange rate of unbonded tokens held in candidates per issued share
|
||||
func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat
|
||||
}
|
||||
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
// move a candidates asset pool from bonded to unbonded pool
|
||||
func (k Keeper) bondedToUnbondedPool(ctx sdk.Context, candidate Candidate) {
|
||||
func (p Pool) bondedToUnbondedPool(candidate Candidate) (Pool, Candidate) {
|
||||
|
||||
// replace bonded shares with unbonded shares
|
||||
tokens := k.removeSharesBonded(ctx, candidate.Assets)
|
||||
candidate.Assets = k.addTokensUnbonded(ctx, tokens)
|
||||
p, tokens := p.removeSharesBonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensUnbonded(tokens)
|
||||
candidate.Status = Unbonded
|
||||
k.setCandidate(ctx, candidate)
|
||||
return p, candidate
|
||||
}
|
||||
|
||||
// move a candidates asset pool from unbonded to bonded pool
|
||||
func (k Keeper) unbondedToBondedPool(ctx sdk.Context, candidate Candidate) {
|
||||
func (p Pool) unbondedToBondedPool(candidate Candidate) (Pool, Candidate) {
|
||||
|
||||
// replace unbonded shares with bonded shares
|
||||
tokens := k.removeSharesUnbonded(ctx, candidate.Assets)
|
||||
candidate.Assets = k.addTokensBonded(ctx, tokens)
|
||||
p, tokens := p.removeSharesUnbonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensBonded(tokens)
|
||||
candidate.Status = Bonded
|
||||
k.setCandidate(ctx, candidate)
|
||||
return p, candidate
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (k Keeper) addTokensBonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
||||
p := k.GetPool(ctx)
|
||||
issuedShares = p.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // (tokens/shares)^-1 * tokens
|
||||
p.BondedPool += amount
|
||||
p.BondedShares = p.BondedShares.Add(issuedShares)
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (k Keeper) removeSharesBonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
||||
p := k.GetPool(ctx)
|
||||
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.BondedPool -= removedTokens
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (k Keeper) addTokensUnbonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
||||
p := k.GetPool(ctx)
|
||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
||||
p.UnbondedPool += amount
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
||||
p := k.GetPool(ctx)
|
||||
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.UnbondedPool -= removedTokens
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// add tokens to a candidate
|
||||
func (k Keeper) candidateAddTokens(ctx sdk.Context, candidate Candidate, amount int64) (issuedDelegatorShares sdk.Rat) {
|
||||
func (p Pool) candidateAddTokens(candidate Candidate,
|
||||
amount int64) (p2 Pool, candidate2 Candidate, issuedDelegatorShares sdk.Rat) {
|
||||
|
||||
p := k.GetPool(ctx)
|
||||
exRate := candidate.delegatorShareExRate()
|
||||
|
||||
var receivedGlobalShares sdk.Rat
|
||||
if candidate.Status == Bonded {
|
||||
receivedGlobalShares = k.addTokensBonded(ctx, amount)
|
||||
p, receivedGlobalShares = p.addTokensBonded(amount)
|
||||
} else {
|
||||
receivedGlobalShares = k.addTokensUnbonded(ctx, amount)
|
||||
p, receivedGlobalShares = p.addTokensUnbonded(amount)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
|
||||
|
||||
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
||||
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
|
||||
k.setPool(ctx, p) // TODO cache Pool?
|
||||
return
|
||||
|
||||
return p, candidate, issuedDelegatorShares
|
||||
}
|
||||
|
||||
// remove shares from a candidate
|
||||
func (k Keeper) candidateRemoveShares(ctx sdk.Context, candidate Candidate, shares sdk.Rat) (createdCoins int64) {
|
||||
func (p Pool) candidateRemoveShares(candidate Candidate,
|
||||
shares sdk.Rat) (p2 Pool, candidate2 Candidate, createdCoins int64) {
|
||||
|
||||
p := k.GetPool(ctx)
|
||||
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
|
||||
|
||||
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
|
||||
if candidate.Status == Bonded {
|
||||
createdCoins = k.removeSharesBonded(ctx, globalPoolSharesToRemove)
|
||||
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
|
||||
} else {
|
||||
createdCoins = k.removeSharesUnbonded(ctx, globalPoolSharesToRemove)
|
||||
p, createdCoins = p.removeSharesUnbonded(globalPoolSharesToRemove)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
|
||||
candidate.Liabilities = candidate.Liabilities.Sub(shares)
|
||||
k.setPool(ctx, p) // TODO cache Pool?
|
||||
return
|
||||
return p, candidate, createdCoins
|
||||
}
|
||||
|
|
|
@ -1,22 +1,374 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
func TestBondedToUnbondedPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
expPool := initialPool()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.OneRat,
|
||||
Liabilities: sdk.OneRat,
|
||||
}
|
||||
poolB, candB := poolA.bondedToUnbondedPool(candA)
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.TotalSupply = 777
|
||||
keeper.setPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
// status unbonded
|
||||
assert.Equal(t, candB.Status, Unbonded)
|
||||
// same exchange rate, assets unchanged
|
||||
assert.Equal(t, candB.Assets, candA.Assets)
|
||||
// bonded pool decreased
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-candA.Assets.Evaluate())
|
||||
// unbonded pool increased
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+candA.Assets.Evaluate())
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||
}
|
||||
|
||||
func TestUnbonbedtoBondedPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.OneRat,
|
||||
Liabilities: sdk.OneRat,
|
||||
}
|
||||
candA.Status = Unbonded
|
||||
poolB, candB := poolA.unbondedToBondedPool(candA)
|
||||
|
||||
// status bonded
|
||||
assert.Equal(t, candB.Status, Bonded)
|
||||
// same exchange rate, assets unchanged
|
||||
assert.Equal(t, candB.Assets, candA.Assets)
|
||||
// bonded pool increased
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+candA.Assets.Evaluate())
|
||||
// unbonded pool decreased
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-candA.Assets.Evaluate())
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||
}
|
||||
|
||||
func TestAddTokensBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
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))
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+10)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
||||
}
|
||||
|
||||
func TestRemoveSharesBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 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.BondedPool, poolA.BondedPool-tokensB)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
||||
}
|
||||
|
||||
func TestAddTokensUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
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))
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+10)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
|
||||
}
|
||||
|
||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 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.UnbondedPool, poolA.UnbondedPool-tokensB)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
|
||||
}
|
||||
|
||||
func TestCandidateAddTokens(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedPool = candA.Assets.Evaluate()
|
||||
poolA.BondedShares = candA.Assets
|
||||
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||
poolB, candB, sharesB := poolA.candidateAddTokens(candA, 10)
|
||||
|
||||
// shares were issued
|
||||
assert.Equal(t, sdk.NewRat(10).Mul(candA.delegatorShareExRate()), sharesB)
|
||||
// pool shares were added
|
||||
assert.Equal(t, candB.Assets, candA.Assets.Add(sdk.NewRat(10)))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.BondedPool, 10+poolA.BondedPool)
|
||||
}
|
||||
|
||||
func TestCandidateRemoveShares(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedPool = candA.Assets.Evaluate()
|
||||
poolA.BondedShares = candA.Assets
|
||||
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||
poolB, candB, coinsB := poolA.candidateRemoveShares(candA, sdk.NewRat(10))
|
||||
|
||||
// coins were created
|
||||
assert.Equal(t, coinsB, int64(10))
|
||||
// pool shares were removed
|
||||
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
// TODO Make all random tests less obfuscated!
|
||||
|
||||
// generate a random candidate
|
||||
func randomCandidate(r *rand.Rand) Candidate {
|
||||
var status CandidateStatus
|
||||
if r.Float64() < float64(0.5) {
|
||||
status = Bonded
|
||||
} else {
|
||||
status = Unbonded
|
||||
}
|
||||
assets := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
liabilities := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
return Candidate{
|
||||
Status: status,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: assets,
|
||||
Liabilities: liabilities,
|
||||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
func randomSetup(r *rand.Rand) (Pool, Candidate) {
|
||||
pool := Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.ZeroRat,
|
||||
UnbondedShares: sdk.ZeroRat,
|
||||
BondedPool: 0,
|
||||
UnbondedPool: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
|
||||
candidate := randomCandidate(r)
|
||||
if candidate.Status == Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
|
||||
pool.BondedPool += candidate.Assets.Evaluate()
|
||||
} else {
|
||||
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
|
||||
pool.UnbondedPool += candidate.Assets.Evaluate()
|
||||
}
|
||||
return pool, candidate
|
||||
}
|
||||
|
||||
func randomTokens(r *rand.Rand) int64 {
|
||||
return int64(r.Int31n(10000))
|
||||
}
|
||||
|
||||
// operation that transforms staking state
|
||||
type Operation func(p Pool, c Candidate) (Pool, Candidate, int64, string)
|
||||
|
||||
// pick a random staking operation
|
||||
func randomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
|
||||
// bond/unbond
|
||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
|
||||
var msg string
|
||||
if cand.Status == Bonded {
|
||||
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
||||
p, cand = p.bondedToUnbondedPool(cand)
|
||||
} else {
|
||||
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
||||
p, cand = p.unbondedToBondedPool(cand)
|
||||
}
|
||||
return p, cand, 0, msg
|
||||
},
|
||||
|
||||
// add some tokens to a candidate
|
||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
|
||||
tokens := int64(r.Int31n(1000))
|
||||
|
||||
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
||||
|
||||
p, cand, _ = p.candidateAddTokens(cand, tokens)
|
||||
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||
},
|
||||
|
||||
// remove some shares from a candidate
|
||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
|
||||
shares := sdk.NewRat(int64(r.Int31n(1000)))
|
||||
|
||||
if shares.GT(cand.Liabilities) {
|
||||
shares = cand.Liabilities.Quo(sdk.NewRat(2))
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
||||
p, cand, tokens := p.candidateRemoveShares(cand, shares)
|
||||
|
||||
msg = fmt.Sprintf("Removed %d shares from %s", shares.Evaluate(), msg)
|
||||
|
||||
return p, cand, tokens, msg
|
||||
},
|
||||
}
|
||||
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 Candidate, pMod Pool, cMod Candidate, tokens int64) {
|
||||
|
||||
// total tokens conserved
|
||||
require.Equal(t,
|
||||
pOrig.UnbondedPool+pOrig.BondedPool,
|
||||
pMod.UnbondedPool+pMod.BondedPool+tokens,
|
||||
"msg: %v\n, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
|
||||
msg,
|
||||
pOrig.UnbondedPool, pOrig.BondedPool,
|
||||
pMod.UnbondedPool, pMod.BondedPool, tokens)
|
||||
|
||||
// nonnegative shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat),
|
||||
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
|
||||
msg, pOrig, pMod, cOrig, cMod, tokens)
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat),
|
||||
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
|
||||
msg, pOrig, pMod, cOrig, cMod, tokens)
|
||||
|
||||
// nonnegative ex rates
|
||||
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat),
|
||||
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||
msg, pMod.bondedShareExRate().Evaluate())
|
||||
|
||||
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat),
|
||||
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||
msg, pMod.unbondedShareExRate().Evaluate())
|
||||
|
||||
// nonnegative ex rate
|
||||
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat),
|
||||
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.PubKey: %s)",
|
||||
msg,
|
||||
cMod.delegatorShareExRate(),
|
||||
cMod.PubKey,
|
||||
)
|
||||
|
||||
// nonnegative assets / liabilities
|
||||
require.False(t, cMod.Assets.LT(sdk.ZeroRat),
|
||||
"Applying operation \"%s\" resulted in negative candidate.Assets: %d (candidate.Liabilities: %d, candidate.PubKey: %s)",
|
||||
msg,
|
||||
cMod.Assets.Evaluate(),
|
||||
cMod.Liabilities.Evaluate(),
|
||||
cMod.PubKey,
|
||||
)
|
||||
|
||||
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat),
|
||||
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %d (candidate.Assets: %d, candidate.PubKey: %s)",
|
||||
msg,
|
||||
cMod.Liabilities.Evaluate(),
|
||||
cMod.Assets.Evaluate(),
|
||||
cMod.PubKey,
|
||||
)
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random state, assert invariants hold
|
||||
func TestIntegrationInvariants(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
|
||||
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
pool, candidates := randomSetup(r1)
|
||||
initialPool, initialCandidates := pool, candidates
|
||||
|
||||
assertInvariants(t, "no operation",
|
||||
initialPool, initialCandidates,
|
||||
pool, candidates, 0)
|
||||
|
||||
for j := 0; j < 100; j++ {
|
||||
|
||||
r2 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
pool, candidates, tokens, msg := randomOperation(r2)(pool, candidates)
|
||||
|
||||
assertInvariants(t, msg,
|
||||
initialPool, initialCandidates,
|
||||
pool, candidates, tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,12 +124,11 @@ func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins
|
|||
ck := bank.NewCoinKeeper(accountMapper)
|
||||
keeper := NewKeeper(ctx, cdc, keyStake, ck)
|
||||
|
||||
//params := paramsNoInflation()
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
// fill all the addresses with some coins
|
||||
for _, addr := range addrs {
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{{params.BondDenom, initCoins}})
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
{keeper.GetParams(ctx).BondDenom, initCoins},
|
||||
})
|
||||
}
|
||||
|
||||
return ctx, accountMapper, keeper
|
||||
|
|
|
@ -2,42 +2,38 @@ package stake
|
|||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
const (
|
||||
hrsPerYear = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 1000000000
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 1000000000
|
||||
)
|
||||
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYear) // as defined by a julian year of 365.25 days
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
|
||||
|
||||
// Tick - called at the end of every block
|
||||
func (k Keeper) Tick(ctx sdk.Context) (change []*abci.Validator, err error) {
|
||||
|
||||
// retrieve params
|
||||
func (k Keeper) Tick(ctx sdk.Context) (change []Validator) {
|
||||
p := k.GetPool(ctx)
|
||||
height := ctx.BlockHeight()
|
||||
|
||||
// Process Validator Provisions
|
||||
// XXX right now just process every 5 blocks, in new SDK make hourly
|
||||
if p.InflationLastTime+5 <= height {
|
||||
p.InflationLastTime = height
|
||||
k.processProvisions(ctx)
|
||||
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
|
||||
if p.InflationLastTime+blockTime >= 3600 {
|
||||
p.InflationLastTime = blockTime
|
||||
p = k.processProvisions(ctx)
|
||||
}
|
||||
|
||||
newVals := k.GetValidators(ctx)
|
||||
// save the params
|
||||
k.setPool(ctx, p)
|
||||
|
||||
// XXX determine change from old validators, set to change
|
||||
_ = newVals
|
||||
return change, nil
|
||||
change = k.getAccUpdateValidators(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// process provisions for an hour period
|
||||
func (k Keeper) processProvisions(ctx sdk.Context) {
|
||||
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
||||
|
||||
pool := k.GetPool(ctx)
|
||||
pool.Inflation = k.nextInflation(ctx).Round(precision)
|
||||
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
|
||||
|
@ -46,9 +42,7 @@ func (k Keeper) processProvisions(ctx sdk.Context) {
|
|||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
|
||||
pool.BondedPool += provisions
|
||||
pool.TotalSupply += provisions
|
||||
|
||||
// save the params
|
||||
k.setPool(ctx, pool)
|
||||
return pool
|
||||
}
|
||||
|
||||
// get the next inflation rate for the hour
|
||||
|
@ -75,5 +69,5 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
|||
inflation = params.InflationMin
|
||||
}
|
||||
|
||||
return
|
||||
return inflation.Round(precision)
|
||||
}
|
||||
|
|
|
@ -1,116 +1,134 @@
|
|||
package stake
|
||||
|
||||
//import (
|
||||
//"testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
//sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
//"github.com/stretchr/testify/assert"
|
||||
//)
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
//func TestGetInflation(t *testing.T) {
|
||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
//params := defaultParams()
|
||||
//keeper.setParams(ctx, params)
|
||||
//gs := keeper.GetPool(ctx)
|
||||
func TestGetInflation(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||
|
||||
//// Governing Mechanism:
|
||||
//// bondedRatio = BondedPool / TotalSupply
|
||||
//// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedPool / TotalSupply
|
||||
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
//tests := []struct {
|
||||
//setBondedPool, setTotalSupply int64
|
||||
//setInflation, expectedChange sdk.Rat
|
||||
//}{
|
||||
//// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||
//{0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYr)},
|
||||
tests := []struct {
|
||||
name string
|
||||
setBondedPool, setTotalSupply int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
}{
|
||||
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||
{"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, sdk.NewRat(20, 100), sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
||||
// 100% bonded, starting at 20% inflation and being reduced
|
||||
// (1 - (1/0.67))*(0.13/8667)
|
||||
{"test 2", 1, 1, 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
|
||||
//{1, 2, sdk.NewRat(10, 100), sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
||||
// 50% bonded, starting at 10% inflation and being increased
|
||||
{"test 3", 1, 2, 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)
|
||||
//{1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
||||
//{1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)},
|
||||
// test 7% minimum stop (testing with 100% bonded)
|
||||
{"test 4", 1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
||||
{"test 5", 1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
|
||||
//// test 20% maximum stop (testing with 0% bonded)
|
||||
//{0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
|
||||
//{0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)},
|
||||
// test 20% maximum stop (testing with 0% bonded)
|
||||
{"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
|
||||
//{67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
||||
//}
|
||||
//for _, tc := range tests {
|
||||
//gs.BondedPool, p.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||
//gs.Inflation = tc.setInflation
|
||||
// perfect balance shouldn't change inflation
|
||||
{"test 8", 67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
pool.BondedPool, pool.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||
pool.Inflation = tc.setInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
//inflation := nextInflation(gs, params)
|
||||
//diffInflation := inflation.Sub(tc.setInflation)
|
||||
inflation := keeper.nextInflation(ctx)
|
||||
diffInflation := inflation.Sub(tc.setInflation)
|
||||
|
||||
//assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
//"%v, %v", diffInflation, tc.expectedChange)
|
||||
//}
|
||||
//}
|
||||
assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
|
||||
}
|
||||
}
|
||||
|
||||
//func TestProcessProvisions(t *testing.T) {
|
||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
//params := defaultParams()
|
||||
//keeper.setParams(ctx, params)
|
||||
//gs := keeper.GetPool(ctx)
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
params := defaultParams()
|
||||
keeper.setParams(ctx, params)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//// create some candidates some bonded, some unbonded
|
||||
//candidates := candidatesFromAddrsEmpty(addrs)
|
||||
//for i, candidate := range candidates {
|
||||
//if i < 5 {
|
||||
//candidate.Status = Bonded
|
||||
//}
|
||||
//mintedTokens := int64((i + 1) * 10000000)
|
||||
//gs.TotalSupply += mintedTokens
|
||||
//keeper.candidateAddTokens(ctx, candidate, mintedTokens)
|
||||
//keeper.setCandidate(ctx, candidate)
|
||||
//}
|
||||
//var totalSupply int64 = 550000000
|
||||
//var bondedShares int64 = 150000000
|
||||
//var unbondedShares int64 = 400000000
|
||||
// create some candidates some bonded, some unbonded
|
||||
candidates := make([]Candidate, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(0),
|
||||
Liabilities: sdk.NewRat(0),
|
||||
}
|
||||
if i < 5 {
|
||||
c.Status = Bonded
|
||||
}
|
||||
mintedTokens := int64((i + 1) * 10000000)
|
||||
pool.TotalSupply += mintedTokens
|
||||
pool, c, _ = pool.candidateAddTokens(c, mintedTokens)
|
||||
|
||||
//// initial bonded ratio ~ 27%
|
||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", p.bondedRatio())
|
||||
keeper.setCandidate(ctx, c)
|
||||
candidates[i] = c
|
||||
}
|
||||
keeper.setPool(ctx, pool)
|
||||
var totalSupply int64 = 550000000
|
||||
var bondedShares int64 = 150000000
|
||||
var unbondedShares int64 = 400000000
|
||||
assert.Equal(t, totalSupply, pool.TotalSupply)
|
||||
assert.Equal(t, bondedShares, pool.BondedPool)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||
|
||||
//// Supplies
|
||||
//assert.Equal(t, totalSupply, p.TotalSupply)
|
||||
//assert.Equal(t, bondedShares, p.BondedPool)
|
||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
||||
// initial bonded ratio ~ 27%
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", pool.bondedRatio())
|
||||
|
||||
//// test the value of candidate shares
|
||||
//assert.True(t, p.bondedShareExRate().Equal(sdk.OneRat), "%v", p.bondedShareExRate())
|
||||
// test the value of candidate shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat), "%v", pool.bondedShareExRate())
|
||||
|
||||
//initialSupply := p.TotalSupply
|
||||
//initialUnbonded := p.TotalSupply - p.BondedPool
|
||||
initialSupply := pool.TotalSupply
|
||||
initialUnbonded := pool.TotalSupply - pool.BondedPool
|
||||
|
||||
//// process the provisions a year
|
||||
//for hr := 0; hr < 8766; hr++ {
|
||||
//expInflation := nextInflation(gs, params).Round(1000000000)
|
||||
//expProvisions := (expInflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr)).Evaluate()
|
||||
//startBondedPool := p.BondedPool
|
||||
//startTotalSupply := p.TotalSupply
|
||||
//processProvisions(ctx, keeper, p, params)
|
||||
//assert.Equal(t, startBondedPool+expProvisions, p.BondedPool)
|
||||
//assert.Equal(t, startTotalSupply+expProvisions, p.TotalSupply)
|
||||
//}
|
||||
//assert.NotEqual(t, initialSupply, p.TotalSupply)
|
||||
//assert.Equal(t, initialUnbonded, p.UnbondedPool)
|
||||
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, p.TotalSupply-gs.BondedPool))
|
||||
// process the provisions a year
|
||||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
expInflation := keeper.nextInflation(ctx).Round(1000000000)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat)).Evaluate()
|
||||
startBondedPool := pool.BondedPool
|
||||
startTotalSupply := pool.TotalSupply
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
//fmt.Printf("hr %v, startBondedPool %v, expProvisions %v, pool.BondedPool %v\n", hr, startBondedPool, expProvisions, pool.BondedPool)
|
||||
require.Equal(t, startBondedPool+expProvisions, pool.BondedPool, "hr %v", hr)
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TotalSupply)
|
||||
}
|
||||
pool = keeper.GetPool(ctx)
|
||||
assert.NotEqual(t, initialSupply, pool.TotalSupply)
|
||||
assert.Equal(t, initialUnbonded, pool.UnbondedPool)
|
||||
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, pool.TotalSupply-pool.BondedPool))
|
||||
|
||||
//// initial bonded ratio ~ 35% ~ 30% increase for bonded holders
|
||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", p.bondedRatio())
|
||||
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(271734723, 671734723)), "%v", pool.bondedRatio())
|
||||
|
||||
//// global supply
|
||||
//assert.Equal(t, int64(611813022), p.TotalSupply)
|
||||
//assert.Equal(t, int64(211813022), p.BondedPool)
|
||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
||||
// global supply
|
||||
assert.Equal(t, int64(671734723), pool.TotalSupply)
|
||||
assert.Equal(t, int64(271734723), pool.BondedPool)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||
|
||||
//// test the value of candidate shares
|
||||
//assert.True(t, p.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", p.bondedShareExRate())
|
||||
// test the value of candidate shares
|
||||
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(271734723)), "%v", pool.bondedShareExRate())
|
||||
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,6 @@ type Pool struct {
|
|||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
}
|
||||
|
||||
// XXX define globalstate interface?
|
||||
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
TotalSupply: 0,
|
||||
|
@ -56,30 +54,6 @@ func initialPool() Pool {
|
|||
}
|
||||
}
|
||||
|
||||
// get the bond ratio of the global state
|
||||
func (p Pool) bondedRatio() sdk.Rat {
|
||||
if p.TotalSupply > 0 {
|
||||
return sdk.NewRat(p.BondedPool, p.TotalSupply)
|
||||
}
|
||||
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.BondedPool).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonded tokens held in candidates per issued share
|
||||
func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat
|
||||
}
|
||||
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________________________________
|
||||
|
||||
// CandidateStatus - status of a validator-candidate
|
||||
|
|
Loading…
Reference in New Issue