Merge PR #1492: Improve Module Test Coverage
* Merge pull request #1492: Improve Module Test Coverage * Revert renaming of SignCheckDeliver [#1492] * Remove named fields from stake unit tests & fix comments [#1492] * update for tmlibs->tendermint/libs * Remove tmlibs dependency
This commit is contained in:
parent
f1194019cd
commit
0b9e0f2afc
|
@ -137,6 +137,7 @@
|
|||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
|
@ -433,7 +434,7 @@
|
|||
"netutil",
|
||||
"trace"
|
||||
]
|
||||
revision = "87b3feba568e144938625fc5d80ec92566c1a8fe"
|
||||
revision = "ed29d75add3d7c4bf7ca65aac0c6df3d1420216f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
|
@ -59,7 +59,7 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
|||
|
||||
Note we utilize the popular [cobra library](https://github.com/spf13/cobra)
|
||||
for the CLI, in concert with the [viper library](https://github.com/spf13/library)
|
||||
for managing configuration. See our [cli library](https://github.com/tendermint/tmlibs/blob/master/cli/setup.go)
|
||||
for managing configuration. See our [cli library](https://github.com/tendermint/blob/master/tmlibs/cli/setup.go)
|
||||
for more details.
|
||||
|
||||
TODO: compile and run the binary
|
||||
|
|
|
@ -4,14 +4,12 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
|
|
|
@ -3,15 +3,13 @@ package cool
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
)
|
||||
|
||||
// Extended ABCI application
|
||||
type App struct {
|
||||
*bam.BaseApp
|
||||
Cdc *wire.Codec // public since the codec is passed into the module anyways.
|
||||
KeyMain *sdk.KVStoreKey
|
||||
KeyAccount *sdk.KVStoreKey
|
||||
|
||||
// TODO: Abstract this out from not needing to be auth specifically
|
||||
AccountMapper auth.AccountMapper
|
||||
FeeCollectionKeeper auth.FeeCollectionKeeper
|
||||
|
||||
GenesisAccounts []auth.Account
|
||||
}
|
||||
|
||||
// partially construct a new app on the memstore for module and genesis testing
|
||||
func NewApp() *App {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
// create the cdc with some standard codecs
|
||||
cdc := wire.NewCodec()
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
|
||||
// create your application object
|
||||
app := &App{
|
||||
BaseApp: bam.NewBaseApp("mock", cdc, logger, db),
|
||||
Cdc: cdc,
|
||||
KeyMain: sdk.NewKVStoreKey("main"),
|
||||
KeyAccount: sdk.NewKVStoreKey("acc"),
|
||||
}
|
||||
|
||||
// define the accountMapper
|
||||
app.AccountMapper = auth.NewAccountMapper(
|
||||
app.Cdc,
|
||||
app.KeyAccount, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
|
||||
// initialize the app, the chainers and blockers can be overwritten before calling complete setup
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.AccountMapper, app.FeeCollectionKeeper))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// complete the application setup after the routes have been registered
|
||||
func (app *App) CompleteSetup(newKeys []*sdk.KVStoreKey) error {
|
||||
newKeys = append(newKeys, app.KeyMain)
|
||||
newKeys = append(newKeys, app.KeyAccount)
|
||||
app.MountStoresIAVL(newKeys...)
|
||||
err := app.LoadLatestVersion(app.KeyMain)
|
||||
return err
|
||||
}
|
||||
|
||||
// custom logic for initialization
|
||||
func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain {
|
||||
|
||||
// load the accounts
|
||||
for _, genacc := range app.GenesisAccounts {
|
||||
acc := app.AccountMapper.NewAccountWithAddress(ctx, genacc.GetAddress())
|
||||
err := acc.SetCoins(genacc.GetCoins())
|
||||
if err != nil {
|
||||
// TODO: Handle with #870
|
||||
panic(err)
|
||||
}
|
||||
app.AccountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
||||
// Generate genesis accounts loaded with coins, and returns their addresses, pubkeys, and privkeys
|
||||
func CreateGenAccounts(numAccs int64, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.Address, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) {
|
||||
for i := int64(0); i < numAccs; i++ {
|
||||
privKey := crypto.GenPrivKeyEd25519()
|
||||
pubKey := privKey.PubKey()
|
||||
addr := pubKey.Address()
|
||||
|
||||
genAcc := &auth.BaseAccount{
|
||||
Address: addr,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
genAccs = append(genAccs, genAcc)
|
||||
privKeys = append(privKeys, privKey)
|
||||
pubKeys = append(pubKeys, pubKey)
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
// A mock transaction that has a validation which can fail.
|
||||
type testMsg struct {
|
||||
signers []sdk.Address
|
||||
positiveNum int64
|
||||
}
|
||||
|
||||
// TODO: Clean this up, make it public
|
||||
const msgType = "testMsg"
|
||||
|
||||
func (tx testMsg) Type() string { return msgType }
|
||||
func (tx testMsg) GetMsg() sdk.Msg { return tx }
|
||||
func (tx testMsg) GetMemo() string { return "" }
|
||||
func (tx testMsg) GetSignBytes() []byte { return nil }
|
||||
func (tx testMsg) GetSigners() []sdk.Address { return tx.signers }
|
||||
func (tx testMsg) GetSignatures() []auth.StdSignature { return nil }
|
||||
func (tx testMsg) ValidateBasic() sdk.Error {
|
||||
if tx.positiveNum >= 0 {
|
||||
return nil
|
||||
}
|
||||
return sdk.ErrTxDecode("positiveNum should be a non-negative integer.")
|
||||
}
|
||||
|
||||
// test auth module messages
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
|
||||
coins = sdk.Coins{sdk.NewCoin("foocoin", 10)}
|
||||
testMsg1 = testMsg{signers: []sdk.Address{addr1}, positiveNum: 1}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *App {
|
||||
mapp := NewApp()
|
||||
|
||||
mapp.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
|
||||
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{}))
|
||||
return mapp
|
||||
}
|
||||
|
||||
func TestMsgPrivKeys(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
mapp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil)
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins := sdk.Coins{sdk.NewCoin("foocoin", 77)}
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
|
||||
// Construct genesis state
|
||||
SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
require.Equal(t, acc1, res1.(*auth.BaseAccount))
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{testMsg1}, []int64{0}, []int64{0}, true, priv1)
|
||||
|
||||
// signing a SendMsg with the wrong privKey should be an auth error
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
tx := GenTx([]sdk.Msg{testMsg1}, []int64{0}, []int64{1}, priv2)
|
||||
res := mapp.Deliver(tx)
|
||||
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the correct priv key should still work
|
||||
res = SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{testMsg1}, []int64{0}, []int64{1}, true, priv1)
|
||||
|
||||
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), res.Code, res.Log)
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
var chainID = "" // TODO
|
||||
|
||||
// set the mock app genesis
|
||||
func SetGenesis(app *App, accs []auth.Account) {
|
||||
|
||||
// pass the accounts in via the application (lazy) instead of through RequestInitChain
|
||||
app.GenesisAccounts = accs
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
// check an account balance
|
||||
func CheckBalance(t *testing.T, app *App, addr sdk.Address, exp sdk.Coins) {
|
||||
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
|
||||
res := app.AccountMapper.GetAccount(ctxCheck, addr)
|
||||
require.Equal(t, exp, res.GetCoins())
|
||||
}
|
||||
|
||||
// generate a signed transaction
|
||||
func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
|
||||
|
||||
// make the transaction free
|
||||
fee := auth.StdFee{
|
||||
sdk.Coins{sdk.NewCoin("foocoin", 0)},
|
||||
100000,
|
||||
}
|
||||
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
memo := "testmemotestmemo"
|
||||
for i, p := range priv {
|
||||
sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: sig,
|
||||
AccountNumber: accnums[i],
|
||||
Sequence: seq[i],
|
||||
}
|
||||
}
|
||||
return auth.NewStdTx(msgs, fee, sigs, memo)
|
||||
}
|
||||
|
||||
// generate a set of signed transactions a msg, that differ only by having the
|
||||
// sequence numbers incremented between every transaction.
|
||||
func GenSequenceOfTxs(msgs []sdk.Msg, accnums []int64, initSeqNums []int64, numToGenerate int, priv ...crypto.PrivKeyEd25519) []auth.StdTx {
|
||||
txs := make([]auth.StdTx, numToGenerate, numToGenerate)
|
||||
for i := 0; i < numToGenerate; i++ {
|
||||
txs[i] = GenTx(msgs, accnums, initSeqNums, priv...)
|
||||
incrementAllSequenceNumbers(initSeqNums)
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func incrementAllSequenceNumbers(initSeqNums []int64) {
|
||||
for i := 0; i < len(initSeqNums); i++ {
|
||||
initSeqNums[i]++
|
||||
}
|
||||
}
|
||||
|
||||
// check a transaction result
|
||||
func SignCheck(app *baseapp.BaseApp, msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.Result {
|
||||
tx := GenTx(msgs, accnums, seq, priv...)
|
||||
res := app.Check(tx)
|
||||
return res
|
||||
}
|
||||
|
||||
// simulate a block
|
||||
func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accnums []int64, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) sdk.Result {
|
||||
|
||||
// Sign the tx
|
||||
tx := GenTx(msgs, accnums, seq, priv...)
|
||||
|
||||
// Run a Check
|
||||
res := app.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = app.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
|
||||
app.Commit()
|
||||
return res
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -12,13 +12,13 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T, numGenAccs int64) (*mock.App, Keeper, stake.Keeper, []sdk.Address, []crypto.PubKey, []crypto.PrivKey) {
|
||||
func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, []sdk.Address, []crypto.PubKey, []crypto.PrivKey) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
stake.RegisterWire(mapp.Cdc)
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
)
|
||||
|
||||
const chainID = ""
|
||||
|
||||
// App extends an ABCI application.
|
||||
type App struct {
|
||||
*bam.BaseApp
|
||||
Cdc *wire.Codec // Cdc is public since the codec is passed into the module anyways
|
||||
KeyMain *sdk.KVStoreKey
|
||||
KeyAccount *sdk.KVStoreKey
|
||||
|
||||
// TODO: Abstract this out from not needing to be auth specifically
|
||||
AccountMapper auth.AccountMapper
|
||||
FeeCollectionKeeper auth.FeeCollectionKeeper
|
||||
|
||||
GenesisAccounts []auth.Account
|
||||
}
|
||||
|
||||
// NewApp partially constructs a new app on the memstore for module and genesis
|
||||
// testing.
|
||||
func NewApp() *App {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
// Create the cdc with some standard codecs
|
||||
cdc := wire.NewCodec()
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
|
||||
// Create your application object
|
||||
app := &App{
|
||||
BaseApp: bam.NewBaseApp("mock", cdc, logger, db),
|
||||
Cdc: cdc,
|
||||
KeyMain: sdk.NewKVStoreKey("main"),
|
||||
KeyAccount: sdk.NewKVStoreKey("acc"),
|
||||
}
|
||||
|
||||
// Define the accountMapper
|
||||
app.AccountMapper = auth.NewAccountMapper(
|
||||
app.Cdc,
|
||||
app.KeyAccount,
|
||||
&auth.BaseAccount{},
|
||||
)
|
||||
|
||||
// Initialize the app. The chainers and blockers can be overwritten before
|
||||
// calling complete setup.
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.AccountMapper, app.FeeCollectionKeeper))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// CompleteSetup completes the application setup after the routes have been
|
||||
// registered.
|
||||
func (app *App) CompleteSetup(newKeys []*sdk.KVStoreKey) error {
|
||||
newKeys = append(newKeys, app.KeyMain)
|
||||
newKeys = append(newKeys, app.KeyAccount)
|
||||
|
||||
app.MountStoresIAVL(newKeys...)
|
||||
err := app.LoadLatestVersion(app.KeyMain)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// InitChainer performs custom logic for initialization.
|
||||
func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain {
|
||||
// Load the genesis accounts
|
||||
for _, genacc := range app.GenesisAccounts {
|
||||
acc := app.AccountMapper.NewAccountWithAddress(ctx, genacc.GetAddress())
|
||||
acc.SetCoins(genacc.GetCoins())
|
||||
app.AccountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
||||
// CreateGenAccounts generates genesis accounts loaded with coins, and returns
|
||||
// their addresses, pubkeys, and privkeys.
|
||||
func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.Address, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) {
|
||||
for i := 0; i < numAccs; i++ {
|
||||
privKey := crypto.GenPrivKeyEd25519()
|
||||
pubKey := privKey.PubKey()
|
||||
addr := pubKey.Address()
|
||||
|
||||
genAcc := &auth.BaseAccount{
|
||||
Address: addr,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
genAccs = append(genAccs, genAcc)
|
||||
privKeys = append(privKeys, privKey)
|
||||
pubKeys = append(pubKeys, pubKey)
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetGenesis sets the mock app genesis accounts.
|
||||
func SetGenesis(app *App, accs []auth.Account) {
|
||||
// Pass the accounts in via the application (lazy) instead of through
|
||||
// RequestInitChain.
|
||||
app.GenesisAccounts = accs
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
// GenTx generates a signed mock transaction.
|
||||
func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKey) auth.StdTx {
|
||||
// Make the transaction free
|
||||
fee := auth.StdFee{
|
||||
sdk.Coins{sdk.NewCoin("foocoin", 0)},
|
||||
100000,
|
||||
}
|
||||
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
memo := "testmemotestmemo"
|
||||
|
||||
for i, p := range priv {
|
||||
sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: sig,
|
||||
AccountNumber: accnums[i],
|
||||
Sequence: seq[i],
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msgs, fee, sigs, memo)
|
||||
}
|
||||
|
||||
// GenSequenceOfTxs generates a set of signed transactions of messages, such
|
||||
// that they differ only by having the sequence numbers incremented between
|
||||
// every transaction.
|
||||
func GenSequenceOfTxs(msgs []sdk.Msg, accnums []int64, initSeqNums []int64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx {
|
||||
txs := make([]auth.StdTx, numToGenerate, numToGenerate)
|
||||
for i := 0; i < numToGenerate; i++ {
|
||||
txs[i] = GenTx(msgs, accnums, initSeqNums, priv...)
|
||||
incrementAllSequenceNumbers(initSeqNums)
|
||||
}
|
||||
|
||||
return txs
|
||||
}
|
||||
|
||||
func incrementAllSequenceNumbers(initSeqNums []int64) {
|
||||
for i := 0; i < len(initSeqNums); i++ {
|
||||
initSeqNums[i]++
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
const msgType = "testMsg"
|
||||
|
||||
var (
|
||||
numAccts = 2
|
||||
genCoins = sdk.Coins{sdk.NewCoin("foocoin", 77)}
|
||||
accs, addrs, pubKeys, privKeys = CreateGenAccounts(numAccts, genCoins)
|
||||
)
|
||||
|
||||
// testMsg is a mock transaction that has a validation which can fail.
|
||||
type testMsg struct {
|
||||
signers []sdk.Address
|
||||
positiveNum int64
|
||||
}
|
||||
|
||||
func (tx testMsg) Type() string { return msgType }
|
||||
func (tx testMsg) GetMsg() sdk.Msg { return tx }
|
||||
func (tx testMsg) GetMemo() string { return "" }
|
||||
func (tx testMsg) GetSignBytes() []byte { return nil }
|
||||
func (tx testMsg) GetSigners() []sdk.Address { return tx.signers }
|
||||
func (tx testMsg) GetSignatures() []auth.StdSignature { return nil }
|
||||
func (tx testMsg) ValidateBasic() sdk.Error {
|
||||
if tx.positiveNum >= 0 {
|
||||
return nil
|
||||
}
|
||||
return sdk.ErrTxDecode("positiveNum should be a non-negative integer.")
|
||||
}
|
||||
|
||||
// getMockApp returns an initialized mock application.
|
||||
func getMockApp(t *testing.T) *App {
|
||||
mApp := NewApp()
|
||||
|
||||
mApp.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
|
||||
require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{}))
|
||||
|
||||
return mApp
|
||||
}
|
||||
|
||||
func TestCheckAndDeliverGenTx(t *testing.T) {
|
||||
mApp := getMockApp(t)
|
||||
mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil)
|
||||
|
||||
SetGenesis(mApp, accs)
|
||||
ctxCheck := mApp.BaseApp.NewContext(true, abci.Header{})
|
||||
|
||||
msg := testMsg{signers: []sdk.Address{addrs[0]}, positiveNum: 1}
|
||||
|
||||
acct := mApp.AccountMapper.GetAccount(ctxCheck, addrs[0])
|
||||
require.Equal(t, accs[0], acct.(*auth.BaseAccount))
|
||||
|
||||
SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()},
|
||||
true, privKeys[0],
|
||||
)
|
||||
|
||||
// Signing a tx with the wrong privKey should result in an auth error
|
||||
res := SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[1].GetAccountNumber()}, []int64{accs[1].GetSequence() + 1},
|
||||
false, privKeys[1],
|
||||
)
|
||||
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// Resigning the tx with the correct privKey should result in an OK result
|
||||
SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence() + 1},
|
||||
true, privKeys[0],
|
||||
)
|
||||
}
|
||||
|
||||
func TestCheckGenTx(t *testing.T) {
|
||||
mApp := getMockApp(t)
|
||||
mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil)
|
||||
|
||||
SetGenesis(mApp, accs)
|
||||
|
||||
msg1 := testMsg{signers: []sdk.Address{addrs[0]}, positiveNum: 1}
|
||||
CheckGenTx(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg1},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()},
|
||||
true, privKeys[0],
|
||||
)
|
||||
|
||||
msg2 := testMsg{signers: []sdk.Address{addrs[0]}, positiveNum: -1}
|
||||
CheckGenTx(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg2},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()},
|
||||
false, privKeys[0],
|
||||
)
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
// CheckBalance checks the balance of an account.
|
||||
func CheckBalance(t *testing.T, app *App, addr sdk.Address, exp sdk.Coins) {
|
||||
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
|
||||
res := app.AccountMapper.GetAccount(ctxCheck, addr)
|
||||
|
||||
require.Equal(t, exp, res.GetCoins())
|
||||
}
|
||||
|
||||
// CheckGenTx checks a generated signed transaction. The result of the check is
|
||||
// compared against the parameter 'expPass'. A test assertion is made using the
|
||||
// parameter 'expPass' against the result. A corresponding result is returned.
|
||||
func CheckGenTx(
|
||||
t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64,
|
||||
seq []int64, expPass bool, priv ...crypto.PrivKey,
|
||||
) sdk.Result {
|
||||
tx := GenTx(msgs, accNums, seq, priv...)
|
||||
res := app.Check(tx)
|
||||
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SignCheckDeliver checks a generated signed transaction and simulates a
|
||||
// block commitment with the given transaction. A test assertion is made using
|
||||
// the parameter 'expPass' against the result. A corresponding result is
|
||||
// returned.
|
||||
func SignCheckDeliver(
|
||||
t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64,
|
||||
seq []int64, expPass bool, priv ...crypto.PrivKey,
|
||||
) sdk.Result {
|
||||
tx := GenTx(msgs, accNums, seq, priv...)
|
||||
res := app.Check(tx)
|
||||
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a sending a transaction and committing a block
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = app.Deliver(tx)
|
||||
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
app.Commit()
|
||||
|
||||
return res
|
||||
}
|
|
@ -5,11 +5,10 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
@ -103,10 +102,9 @@ func TestSlashingMsgs(t *testing.T) {
|
|||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
unrevokeMsg := MsgUnrevoke{ValidatorAddr: validator.PubKey.Address()}
|
||||
|
||||
// no signing info yet
|
||||
checkValidatorSigningInfo(t, mapp, keeper, addr1, false)
|
||||
|
||||
// unrevoke should fail with validator not revoked
|
||||
res := mock.SignCheck(mapp.BaseApp, []sdk.Msg{unrevokeMsg}, []int64{0}, []int64{1}, priv1)
|
||||
// unrevoke should fail with unknown validator
|
||||
res := mock.CheckGenTx(t, mapp.BaseApp, []sdk.Msg{unrevokeMsg}, []int64{0}, []int64{1}, false, priv1)
|
||||
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), res.Code)
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
@ -22,81 +20,86 @@ var (
|
|||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{{"foocoin", sdk.NewInt(0)}},
|
||||
100000,
|
||||
}
|
||||
coins = sdk.NewCoin("foocoin", 10)
|
||||
fee = auth.StdFee{sdk.Coins{sdk.NewCoin("foocoin", 0)}, 100000}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
// getMockApp returns an initialized mock application for this module.
|
||||
func getMockApp(t *testing.T) (*mock.App, Keeper) {
|
||||
mapp := mock.NewApp()
|
||||
mApp := mock.NewApp()
|
||||
|
||||
RegisterWire(mApp.Cdc)
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
keeper := NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
||||
mapp.Router().AddRoute("stake", NewHandler(keeper))
|
||||
coinKeeper := bank.NewKeeper(mApp.AccountMapper)
|
||||
keeper := NewKeeper(mApp.Cdc, keyStake, coinKeeper, mApp.RegisterCodespace(DefaultCodespace))
|
||||
|
||||
mapp.SetEndBlocker(getEndBlocker(keeper))
|
||||
mapp.SetInitChainer(getInitChainer(mapp, keeper))
|
||||
mApp.Router().AddRoute("stake", NewHandler(keeper))
|
||||
mApp.SetEndBlocker(getEndBlocker(keeper))
|
||||
mApp.SetInitChainer(getInitChainer(mApp, keeper))
|
||||
|
||||
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake}))
|
||||
return mapp, keeper
|
||||
require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{keyStake}))
|
||||
return mApp, keeper
|
||||
}
|
||||
|
||||
// stake endblocker
|
||||
// getEndBlocker returns a stake endblocker.
|
||||
func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
||||
return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
validatorUpdates := EndBlocker(ctx, keeper)
|
||||
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: validatorUpdates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite the mock init chainer
|
||||
// getInitChainer initializes the chainer of the mock app and sets the genesis
|
||||
// state. It returns an empty ResponseInitChain.
|
||||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
|
||||
stakeGenesis := DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
|
||||
InitGenesis(ctx, keeper, stakeGenesis)
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
//__________________________________________________________________________________________
|
||||
|
||||
func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper,
|
||||
addr sdk.Address, expFound bool) Validator {
|
||||
|
||||
func checkValidator(
|
||||
t *testing.T, mapp *mock.App, keeper Keeper,
|
||||
addr sdk.Address, expFound bool,
|
||||
) Validator {
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
validator, found := keeper.GetValidator(ctxCheck, addr1)
|
||||
|
||||
require.Equal(t, expFound, found)
|
||||
return validator
|
||||
}
|
||||
|
||||
func checkDelegation(t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
|
||||
validatorAddr sdk.Address, expFound bool, expShares sdk.Rat) {
|
||||
|
||||
func checkDelegation(
|
||||
t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
|
||||
validatorAddr sdk.Address, expFound bool, expShares sdk.Rat,
|
||||
) {
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr)
|
||||
if expFound {
|
||||
require.True(t, found)
|
||||
assert.True(sdk.RatEq(t, expShares, delegation.Shares))
|
||||
require.True(sdk.RatEq(t, expShares, delegation.Shares))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestStakeMsgs(t *testing.T) {
|
||||
mapp, keeper := getMockApp(t)
|
||||
mApp, keeper := getMockApp(t)
|
||||
|
||||
genCoin := sdk.Coin{"steak", sdk.NewInt(42)}
|
||||
bondCoin := sdk.Coin{"steak", sdk.NewInt(10)}
|
||||
genCoin := sdk.NewCoin("steak", 42)
|
||||
bondCoin := sdk.NewCoin("steak", 10)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
|
@ -108,56 +111,51 @@ func TestStakeMsgs(t *testing.T) {
|
|||
}
|
||||
accs := []auth.Account{acc1, acc2}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
|
||||
////////////////////
|
||||
// Create Validator
|
||||
mock.SetGenesis(mApp, accs)
|
||||
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin})
|
||||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin})
|
||||
|
||||
// create validator
|
||||
description := NewDescription("foo_moniker", "", "", "")
|
||||
createValidatorMsg := NewMsgCreateValidator(
|
||||
addr1, priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
validator := checkValidator(t, mapp, keeper, addr1, true)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mApp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
validator := checkValidator(t, mApp, keeper, addr1, true)
|
||||
require.Equal(t, addr1, validator.Owner)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
|
||||
// check the bond that should have been created as well
|
||||
checkDelegation(t, mapp, keeper, addr1, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Edit Validator
|
||||
checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10))
|
||||
|
||||
// edit the validator
|
||||
description = NewDescription("bar_moniker", "", "", "")
|
||||
editValidatorMsg := NewMsgEditValidator(addr1, description)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{1}, true, priv1)
|
||||
validator = checkValidator(t, mapp, keeper, addr1, true)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{1}, true, priv1)
|
||||
validator = checkValidator(t, mApp, keeper, addr1, true)
|
||||
require.Equal(t, description, validator.Description)
|
||||
|
||||
////////////////////
|
||||
// Delegate
|
||||
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
// delegate
|
||||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin})
|
||||
delegateMsg := NewMsgDelegate(addr2, addr1, bondCoin)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{0}, true, priv2)
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Begin Unbonding
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{0}, true, priv2)
|
||||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewRat(10))
|
||||
|
||||
// begin unbonding
|
||||
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10))
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
|
||||
// delegation should exist anymore
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
|
||||
checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Rat{})
|
||||
|
||||
// balance should be the same because bonding not yet complete
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
}
|
||||
|
|
|
@ -1,50 +1,58 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
// InitGenesis sets the pool and parameters for the provided keeper and
|
||||
// initializes the IntraTxCounter. For each validator in data, it sets that
|
||||
// validator in the keeper along with manually setting the indexes. In
|
||||
// addition, it also sets any delegations found in data. Finally, it updates
|
||||
// the bonded validators.
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
|
||||
keeper.SetPool(ctx, data.Pool)
|
||||
keeper.SetNewParams(ctx, data.Params)
|
||||
keeper.InitIntraTxCounter(ctx)
|
||||
for _, validator := range data.Validators {
|
||||
|
||||
// set validator
|
||||
for _, validator := range data.Validators {
|
||||
keeper.SetValidator(ctx, validator)
|
||||
|
||||
// manually set indexes for the first time
|
||||
// Manually set indexes for the first time
|
||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
|
||||
if validator.Status() == sdk.Bonded {
|
||||
keeper.SetValidatorBondedIndex(ctx, validator)
|
||||
}
|
||||
}
|
||||
|
||||
for _, bond := range data.Bonds {
|
||||
keeper.SetDelegation(ctx, bond)
|
||||
}
|
||||
|
||||
keeper.UpdateBondedValidatorsFull(ctx)
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
// WriteGenesis returns a GenesisState for a given context and keeper. The
|
||||
// GenesisState will contain the pool, params, validators, and bonds found in
|
||||
// the keeper.
|
||||
func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
validators := keeper.GetAllValidators(ctx)
|
||||
bonds := keeper.GetAllDelegations(ctx)
|
||||
|
||||
return types.GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
bonds,
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteValidators - output current validator set
|
||||
// WriteValidators returns a slice of bonded genesis validators.
|
||||
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
vals = append(vals, tmtypes.GenesisValidator{
|
||||
|
@ -52,7 +60,9 @@ func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisVali
|
|||
Power: validator.GetPower().RoundInt64(),
|
||||
Name: validator.GetMoniker(),
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,30 +7,29 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// keeper
|
||||
type Keeper = keeper.Keeper
|
||||
|
||||
var NewKeeper = keeper.NewKeeper
|
||||
|
||||
// types
|
||||
type Validator = types.Validator
|
||||
type Description = types.Description
|
||||
type Delegation = types.Delegation
|
||||
type UnbondingDelegation = types.UnbondingDelegation
|
||||
type Redelegation = types.Redelegation
|
||||
type Params = types.Params
|
||||
type Pool = types.Pool
|
||||
type PoolShares = types.PoolShares
|
||||
type MsgCreateValidator = types.MsgCreateValidator
|
||||
type MsgEditValidator = types.MsgEditValidator
|
||||
type MsgDelegate = types.MsgDelegate
|
||||
type MsgBeginUnbonding = types.MsgBeginUnbonding
|
||||
type MsgCompleteUnbonding = types.MsgCompleteUnbonding
|
||||
type MsgBeginRedelegate = types.MsgBeginRedelegate
|
||||
type MsgCompleteRedelegate = types.MsgCompleteRedelegate
|
||||
type GenesisState = types.GenesisState
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
Validator = types.Validator
|
||||
Description = types.Description
|
||||
Delegation = types.Delegation
|
||||
UnbondingDelegation = types.UnbondingDelegation
|
||||
Redelegation = types.Redelegation
|
||||
Params = types.Params
|
||||
Pool = types.Pool
|
||||
PoolShares = types.PoolShares
|
||||
MsgCreateValidator = types.MsgCreateValidator
|
||||
MsgEditValidator = types.MsgEditValidator
|
||||
MsgDelegate = types.MsgDelegate
|
||||
MsgBeginUnbonding = types.MsgBeginUnbonding
|
||||
MsgCompleteUnbonding = types.MsgCompleteUnbonding
|
||||
MsgBeginRedelegate = types.MsgBeginRedelegate
|
||||
MsgCompleteRedelegate = types.MsgCompleteRedelegate
|
||||
GenesisState = types.GenesisState
|
||||
)
|
||||
|
||||
var (
|
||||
NewKeeper = keeper.NewKeeper
|
||||
|
||||
GetValidatorKey = keeper.GetValidatorKey
|
||||
GetValidatorByPubKeyIndexKey = keeper.GetValidatorByPubKeyIndexKey
|
||||
GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey
|
||||
|
@ -72,7 +71,6 @@ var (
|
|||
DefaultGenesisState = types.DefaultGenesisState
|
||||
RegisterWire = types.RegisterWire
|
||||
|
||||
// messages
|
||||
NewMsgCreateValidator = types.NewMsgCreateValidator
|
||||
NewMsgEditValidator = types.NewMsgEditValidator
|
||||
NewMsgDelegate = types.NewMsgDelegate
|
||||
|
@ -82,7 +80,6 @@ var (
|
|||
NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate
|
||||
)
|
||||
|
||||
// errors
|
||||
const (
|
||||
DefaultCodespace = types.DefaultCodespace
|
||||
CodeInvalidValidator = types.CodeInvalidValidator
|
||||
|
@ -126,7 +123,6 @@ var (
|
|||
ErrMissingSignature = types.ErrMissingSignature
|
||||
)
|
||||
|
||||
// tags
|
||||
var (
|
||||
ActionCreateValidator = tags.ActionCreateValidator
|
||||
ActionEditValidator = tags.ActionEditValidator
|
||||
|
|
|
@ -17,7 +17,7 @@ type Delegation struct {
|
|||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
// two are equal
|
||||
// Equal returns a boolean determining if two Delegation types are identical.
|
||||
func (d Delegation) Equal(d2 Delegation) bool {
|
||||
return bytes.Equal(d.DelegatorAddr, d2.DelegatorAddr) &&
|
||||
bytes.Equal(d.ValidatorAddr, d2.ValidatorAddr) &&
|
||||
|
@ -33,16 +33,20 @@ func (d Delegation) GetDelegator() sdk.Address { return d.DelegatorAddr }
|
|||
func (d Delegation) GetValidator() sdk.Address { return d.ValidatorAddr }
|
||||
func (d Delegation) GetBondShares() sdk.Rat { return d.Shares }
|
||||
|
||||
//Human Friendly pretty printer
|
||||
// HumanReadableString returns a human readable string representation of a
|
||||
// Delegation. An error is returned if the Delegation's delegator or validator
|
||||
// addresses cannot be Bech32 encoded.
|
||||
func (d Delegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bechVal, err := sdk.Bech32ifyAcc(d.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp := "Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
|
@ -50,12 +54,9 @@ func (d Delegation) HumanReadableString() (string, error) {
|
|||
resp += fmt.Sprintf("Height: %d", d.Height)
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// element stored to represent the passive unbonding queue
|
||||
// UnbondingDelegation reflects a delegation's passive unbonding queue.
|
||||
type UnbondingDelegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"` // delegator
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"` // validator unbonding from owner addr
|
||||
|
@ -65,23 +66,28 @@ type UnbondingDelegation struct {
|
|||
Balance sdk.Coin `json:"balance"` // atoms to receive at completion
|
||||
}
|
||||
|
||||
// nolint
|
||||
// Equal returns a boolean determining if two UnbondingDelegation types are
|
||||
// identical.
|
||||
func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&d)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&d2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
//Human Friendly pretty printer
|
||||
// HumanReadableString returns a human readable string representation of an
|
||||
// UnbondingDelegation. An error is returned if the UnbondingDelegation's
|
||||
// delegator or validator addresses cannot be Bech32 encoded.
|
||||
func (d UnbondingDelegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bechVal, err := sdk.Bech32ifyAcc(d.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp := "Unbonding Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
|
@ -93,9 +99,7 @@ func (d UnbondingDelegation) HumanReadableString() (string, error) {
|
|||
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// element stored to represent the passive redelegation queue
|
||||
// Redelegation reflects a delegation's passive re-delegation queue.
|
||||
type Redelegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"` // delegator
|
||||
ValidatorSrcAddr sdk.Address `json:"validator_src_addr"` // validator redelegation source owner addr
|
||||
|
@ -108,27 +112,32 @@ type Redelegation struct {
|
|||
SharesDst sdk.Rat `json:"shares_dst"` // amount of destination shares redelegating
|
||||
}
|
||||
|
||||
// nolint
|
||||
// Equal returns a boolean determining if two Redelegation types are identical.
|
||||
func (d Redelegation) Equal(d2 Redelegation) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&d)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&d2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
//Human Friendly pretty printer
|
||||
// HumanReadableString returns a human readable string representation of a
|
||||
// Redelegation. An error is returned if the UnbondingDelegation's delegator or
|
||||
// validator addresses cannot be Bech32 encoded.
|
||||
func (d Redelegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(d.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bechValSrc, err := sdk.Bech32ifyAcc(d.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bechValDst, err := sdk.Bech32ifyAcc(d.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp := "Redelegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Source Validator: %s\n", bechValSrc)
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDelegationEqual(t *testing.T) {
|
||||
d1 := Delegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewRat(100),
|
||||
}
|
||||
d2 := Delegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewRat(100),
|
||||
}
|
||||
|
||||
ok := d1.Equal(d2)
|
||||
require.True(t, ok)
|
||||
|
||||
d2.ValidatorAddr = addr3
|
||||
d2.Shares = sdk.NewRat(200)
|
||||
|
||||
ok = d1.Equal(d2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestDelegationHumanReadableString(t *testing.T) {
|
||||
d := Delegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewRat(100),
|
||||
}
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
valStr, err := d.HumanReadableString()
|
||||
require.Nil(t, err)
|
||||
require.NotEmpty(t, valStr)
|
||||
}
|
||||
|
||||
func TestUnbondingDelegationEqual(t *testing.T) {
|
||||
ud1 := UnbondingDelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
ud2 := UnbondingDelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
|
||||
ok := ud1.Equal(ud2)
|
||||
require.True(t, ok)
|
||||
|
||||
ud2.ValidatorAddr = addr3
|
||||
ud2.MinTime = 20 * 20 * 2
|
||||
|
||||
ok = ud1.Equal(ud2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestUnbondingDelegationHumanReadableString(t *testing.T) {
|
||||
ud := UnbondingDelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
valStr, err := ud.HumanReadableString()
|
||||
require.Nil(t, err)
|
||||
require.NotEmpty(t, valStr)
|
||||
}
|
||||
|
||||
func TestRedelegationEqual(t *testing.T) {
|
||||
r1 := Redelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
}
|
||||
r2 := Redelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
}
|
||||
|
||||
ok := r1.Equal(r2)
|
||||
require.True(t, ok)
|
||||
|
||||
r2.SharesDst = sdk.NewRat(10)
|
||||
r2.SharesSrc = sdk.NewRat(20)
|
||||
r2.MinTime = 20 * 20 * 2
|
||||
|
||||
ok = r1.Equal(r2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestRedelegationHumanReadableString(t *testing.T) {
|
||||
r := Redelegation{
|
||||
DelegatorAddr: addr1,
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
SharesDst: sdk.NewRat(10),
|
||||
SharesSrc: sdk.NewRat(20),
|
||||
}
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
valStr, err := r.HumanReadableString()
|
||||
require.Nil(t, err)
|
||||
require.NotEmpty(t, valStr)
|
||||
}
|
|
@ -25,97 +25,118 @@ const (
|
|||
func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil")
|
||||
}
|
||||
|
||||
func ErrNoValidatorFound(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
|
||||
func ErrValidatorAlreadyExists(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator")
|
||||
}
|
||||
|
||||
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently revoked")
|
||||
}
|
||||
|
||||
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "error removing validator")
|
||||
}
|
||||
|
||||
func ErrDescriptionLength(codespace sdk.CodespaceType, descriptor string, got, max int) sdk.Error {
|
||||
msg := fmt.Sprintf("bad description length for %v, got length %v, max is %v", descriptor, got, max)
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, msg)
|
||||
}
|
||||
|
||||
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission must be positive")
|
||||
}
|
||||
|
||||
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
|
||||
}
|
||||
|
||||
// delegation
|
||||
func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil")
|
||||
}
|
||||
|
||||
func ErrBadDenom(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "invalid coin denomination")
|
||||
}
|
||||
|
||||
func ErrBadDelegationAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "amount must be > 0")
|
||||
}
|
||||
|
||||
func ErrNoDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no delegation for this (address, validator) pair")
|
||||
}
|
||||
|
||||
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not exist for that address")
|
||||
}
|
||||
|
||||
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not contain this delegation")
|
||||
}
|
||||
|
||||
func ErrInsufficientShares(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "insufficient delegation shares")
|
||||
}
|
||||
|
||||
func ErrDelegationValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "cannot delegate to an empty validator")
|
||||
}
|
||||
|
||||
func ErrNotEnoughDelegationShares(codespace sdk.CodespaceType, shares string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, fmt.Sprintf("not enough shares only have %v", shares))
|
||||
}
|
||||
|
||||
func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0")
|
||||
}
|
||||
|
||||
func ErrBadSharesPrecision(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation,
|
||||
fmt.Sprintf("shares denominator must be < %s, try reducing the number of decimal points",
|
||||
maximumBondingRationalDenominator.String()),
|
||||
)
|
||||
}
|
||||
|
||||
func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1")
|
||||
}
|
||||
|
||||
// redelegation
|
||||
func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min int64) sdk.Error {
|
||||
msg := fmt.Sprintf("%v is not mature requires a min %v of %v, currently it is %v",
|
||||
operation, descriptor, got, min)
|
||||
return sdk.NewError(codespace, CodeUnauthorized, msg)
|
||||
}
|
||||
|
||||
func ErrNoUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no unbonding delegation found")
|
||||
}
|
||||
|
||||
func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found")
|
||||
}
|
||||
|
||||
func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found")
|
||||
}
|
||||
|
||||
func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation,
|
||||
"redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation")
|
||||
}
|
||||
|
||||
// messages
|
||||
func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided")
|
||||
}
|
||||
|
||||
func ErrNeitherShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "neither shares amount nor shares percent provided")
|
||||
}
|
||||
|
||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "missing signature")
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
coinPos = sdk.Coin{"steak", sdk.NewInt(1000)}
|
||||
coinZero = sdk.Coin{"steak", sdk.NewInt(0)}
|
||||
coinNeg = sdk.Coin{"steak", sdk.NewInt(-10000)}
|
||||
coinPos = sdk.NewCoin("steak", 1000)
|
||||
coinZero = sdk.NewCoin("steak", 0)
|
||||
coinNeg = sdk.NewCoin("steak", -10000)
|
||||
)
|
||||
|
||||
// test ValidateBasic for MsgCreateValidator
|
||||
|
@ -197,29 +197,3 @@ func TestMsgCompleteUnbonding(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO introduce with go-amino
|
||||
//func TestSerializeMsg(t *testing.T) {
|
||||
|
||||
//// make sure all types construct properly
|
||||
//bondAmt := 1234321
|
||||
//bond := sdk.Coin{Denom: "atom", Amount: int64(bondAmt)}
|
||||
|
||||
//tests := []struct {
|
||||
//tx sdk.Msg
|
||||
//}{
|
||||
//{NewMsgCreateValidator(addr1, pk1, bond, Description{})},
|
||||
//{NewMsgEditValidator(addr1, Description{})},
|
||||
//{NewMsgDelegate(addr1, addr2, bond)},
|
||||
//{NewMsgUnbond(addr1, addr2, strconv.Itoa(bondAmt))},
|
||||
//}
|
||||
|
||||
//for i, tc := range tests {
|
||||
//var tx sdk.Tx
|
||||
//bs := wire.BinaryBytes(tc.tx)
|
||||
//err := wire.ReadBinaryBytes(bs, &tx)
|
||||
//if require.NoError(t, err, "%d", i) {
|
||||
//require.Equal(t, tc.tx, tx, "%d", i)
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
|
|
@ -6,6 +6,10 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// defaultUnbondingTime reflects three weeks in seconds as the default
|
||||
// unbonding time.
|
||||
const defaultUnbondingTime int64 = 60 * 60 * 24 * 3
|
||||
|
||||
// Params defines the high level settings for staking
|
||||
type Params struct {
|
||||
InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate
|
||||
|
@ -19,21 +23,21 @@ type Params struct {
|
|||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
}
|
||||
|
||||
// nolint
|
||||
// Equal returns a boolean determining if two Param types are identical.
|
||||
func (p Params) Equal(p2 Params) bool {
|
||||
bz1 := MsgCdc.MustMarshalBinary(&p)
|
||||
bz2 := MsgCdc.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
// default params
|
||||
// DefaultParams returns a default set of parameters.
|
||||
func DefaultParams() Params {
|
||||
return Params{
|
||||
InflationRateChange: sdk.NewRat(13, 100),
|
||||
InflationMax: sdk.NewRat(20, 100),
|
||||
InflationMin: sdk.NewRat(7, 100),
|
||||
GoalBonded: sdk.NewRat(67, 100),
|
||||
UnbondingTime: 60 * 60 * 24 * 3, // 3 weeks in seconds
|
||||
UnbondingTime: defaultUnbondingTime,
|
||||
MaxValidators: 100,
|
||||
BondDenom: "steak",
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParamsEqual(t *testing.T) {
|
||||
p1 := DefaultParams()
|
||||
p2 := DefaultParams()
|
||||
|
||||
ok := p1.Equal(p2)
|
||||
require.True(t, ok)
|
||||
|
||||
p2.UnbondingTime = 60 * 60 * 24 * 2
|
||||
p2.BondDenom = "soup"
|
||||
|
||||
ok = p1.Equal(p2)
|
||||
require.False(t, ok)
|
||||
}
|
|
@ -3,11 +3,24 @@ package types
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPoolEqual(t *testing.T) {
|
||||
p1 := InitialPool()
|
||||
p2 := InitialPool()
|
||||
|
||||
ok := p1.Equal(p2)
|
||||
require.True(t, ok)
|
||||
|
||||
p2.BondedTokens = 3
|
||||
p2.BondedShares = sdk.NewRat(10)
|
||||
|
||||
ok = p1.Equal(p2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestBondedRatio(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 1
|
||||
|
@ -62,10 +75,10 @@ func TestUnbondedShareExRate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddTokensBonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
require.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
poolB, sharesB := poolA.addTokensBonded(10)
|
||||
require.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
|
@ -78,10 +91,10 @@ func TestAddTokensBonded(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRemoveSharesBonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
require.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
|
||||
require.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
||||
|
||||
|
@ -94,10 +107,10 @@ func TestRemoveSharesBonded(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddTokensUnbonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.LooseTokens = 10
|
||||
require.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
poolB, sharesB := poolA.addTokensUnbonded(10)
|
||||
require.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
|
@ -110,11 +123,11 @@ func TestAddTokensUnbonded(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||
|
||||
poolA := InitialPool()
|
||||
poolA.UnbondedTokens = 10
|
||||
poolA.UnbondedShares = sdk.NewRat(10)
|
||||
require.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
|
||||
require.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
|
|
|
@ -4,18 +4,19 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// pool shares held by a validator
|
||||
// PoolShares reflects the shares of a validator in a pool.
|
||||
type PoolShares struct {
|
||||
Status sdk.BondStatus `json:"status"`
|
||||
Amount sdk.Rat `json:"amount"` // total shares of type ShareKind
|
||||
Amount sdk.Rat `json:"amount"`
|
||||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
// Equal returns a boolean determining of two PoolShares are identical.
|
||||
func (s PoolShares) Equal(s2 PoolShares) bool {
|
||||
return s.Status == s2.Status &&
|
||||
s.Amount.Equal(s2.Amount)
|
||||
}
|
||||
|
||||
// NewUnbondedShares returns a new PoolShares with a specified unbonded amount.
|
||||
func NewUnbondedShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Unbonded,
|
||||
|
@ -23,6 +24,8 @@ func NewUnbondedShares(amount sdk.Rat) PoolShares {
|
|||
}
|
||||
}
|
||||
|
||||
// NewUnbondingShares returns a new PoolShares with a specified unbonding
|
||||
// amount.
|
||||
func NewUnbondingShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Unbonding,
|
||||
|
@ -30,6 +33,7 @@ func NewUnbondingShares(amount sdk.Rat) PoolShares {
|
|||
}
|
||||
}
|
||||
|
||||
// NewBondedShares returns a new PoolSahres with a specified bonding amount.
|
||||
func NewBondedShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Bonded,
|
||||
|
@ -37,9 +41,7 @@ func NewBondedShares(amount sdk.Rat) PoolShares {
|
|||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// amount of unbonded shares
|
||||
// Unbonded returns the amount of unbonded shares.
|
||||
func (s PoolShares) Unbonded() sdk.Rat {
|
||||
if s.Status == sdk.Unbonded {
|
||||
return s.Amount
|
||||
|
@ -47,7 +49,7 @@ func (s PoolShares) Unbonded() sdk.Rat {
|
|||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// amount of unbonding shares
|
||||
// Unbonding returns the amount of unbonding shares.
|
||||
func (s PoolShares) Unbonding() sdk.Rat {
|
||||
if s.Status == sdk.Unbonding {
|
||||
return s.Amount
|
||||
|
@ -55,7 +57,7 @@ func (s PoolShares) Unbonding() sdk.Rat {
|
|||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// amount of bonded shares
|
||||
// Bonded returns amount of bonded shares.
|
||||
func (s PoolShares) Bonded() sdk.Rat {
|
||||
if s.Status == sdk.Bonded {
|
||||
return s.Amount
|
||||
|
@ -63,64 +65,80 @@ func (s PoolShares) Bonded() sdk.Rat {
|
|||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// equivalent amount of shares if the shares were unbonded
|
||||
// ToUnbonded returns the equivalent amount of pool shares if the shares were
|
||||
// unbonded.
|
||||
func (s PoolShares) ToUnbonded(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondedShareExRate()) // (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondedshr/bondedshr = unbondedshr
|
||||
// (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondedShareExRate())
|
||||
// bondedshr*unbondedshr/bondedshr = unbondedshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
case sdk.Unbonding:
|
||||
exRate := p.UnbondingShareExRate().Quo(p.UnbondedShareExRate()) // (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
||||
amount = s.Amount.Mul(exRate) // unbondingshr*unbondedshr/unbondingshr = unbondedshr
|
||||
// (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
||||
exRate := p.UnbondingShareExRate().Quo(p.UnbondedShareExRate())
|
||||
// unbondingshr*unbondedshr/unbondingshr = unbondedshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
case sdk.Unbonded:
|
||||
amount = s.Amount
|
||||
}
|
||||
|
||||
return NewUnbondedShares(amount)
|
||||
}
|
||||
|
||||
// equivalent amount of shares if the shares were unbonding
|
||||
// ToUnbonding returns the equivalent amount of pool shares if the shares were
|
||||
// unbonding.
|
||||
func (s PoolShares) ToUnbonding(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondingShareExRate()) // (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondingshr/bondedshr = unbondingshr
|
||||
// (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
||||
exRate := p.BondedShareExRate().Quo(p.UnbondingShareExRate())
|
||||
// bondedshr*unbondingshr/bondedshr = unbondingshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
case sdk.Unbonding:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonded:
|
||||
exRate := p.UnbondedShareExRate().Quo(p.UnbondingShareExRate()) // (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
||||
amount = s.Amount.Mul(exRate) // unbondedshr*unbondingshr/unbondedshr = unbondingshr
|
||||
// (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
||||
exRate := p.UnbondedShareExRate().Quo(p.UnbondingShareExRate())
|
||||
// unbondedshr*unbondingshr/unbondedshr = unbondingshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
}
|
||||
|
||||
return NewUnbondingShares(amount)
|
||||
}
|
||||
|
||||
// equivalent amount of shares if the shares were bonded
|
||||
// ToBonded the equivalent amount of pool shares if the shares were bonded.
|
||||
func (s PoolShares) ToBonded(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonding:
|
||||
exRate := p.UnbondingShareExRate().Quo(p.BondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
// (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
exRate := p.UnbondingShareExRate().Quo(p.BondedShareExRate())
|
||||
// ubshr*bshr/ubshr = bshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
case sdk.Unbonded:
|
||||
exRate := p.UnbondedShareExRate().Quo(p.BondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
// (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
exRate := p.UnbondedShareExRate().Quo(p.BondedShareExRate())
|
||||
// ubshr*bshr/ubshr = bshr
|
||||
amount = s.Amount.Mul(exRate)
|
||||
}
|
||||
|
||||
return NewUnbondedShares(amount)
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// TODO better tests
|
||||
// get the equivalent amount of tokens contained by the shares
|
||||
// Tokens returns the equivalent amount of tokens contained by the pool shares
|
||||
// for a given pool.
|
||||
func (s PoolShares) Tokens(p Pool) sdk.Rat {
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
return p.BondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
return p.BondedShareExRate().Mul(s.Amount)
|
||||
case sdk.Unbonding:
|
||||
return p.UnbondingShareExRate().Mul(s.Amount)
|
||||
case sdk.Unbonded:
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPoolSharesTokens(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
|
||||
val := Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
||||
DelegatorShares: sdk.NewRat(100),
|
||||
}
|
||||
|
||||
pool.BondedTokens = val.PoolShares.Bonded().RoundInt64()
|
||||
pool.BondedShares = val.PoolShares.Bonded()
|
||||
|
||||
poolShares := NewBondedShares(sdk.NewRat(50))
|
||||
tokens := poolShares.Tokens(pool)
|
||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
||||
|
||||
poolShares = NewUnbondingShares(sdk.NewRat(50))
|
||||
tokens = poolShares.Tokens(pool)
|
||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
||||
|
||||
poolShares = NewUnbondedShares(sdk.NewRat(50))
|
||||
tokens = poolShares.Tokens(pool)
|
||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
||||
}
|
|
@ -11,7 +11,6 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// dummy pubkeys/addresses
|
||||
pk1 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk2 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
pk3 = crypto.GenPrivKeyEd25519().PubKey()
|
||||
|
@ -23,18 +22,20 @@ var (
|
|||
emptyPubkey crypto.PubKey
|
||||
)
|
||||
|
||||
//______________________________________________________________
|
||||
|
||||
// any operation that transforms staking state
|
||||
// takes in RNG instance, pool, validator
|
||||
// returns updated pool, updated validator, delta tokens, descriptive message
|
||||
// Operation reflects any operation that transforms staking state. It takes in
|
||||
// a RNG instance, pool, validator and returns an updated pool, updated
|
||||
// validator, delta tokens, and descriptive message.
|
||||
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, int64, string)
|
||||
|
||||
// operation: bond or unbond a validator depending on current status
|
||||
// OpBondOrUnbond implements an operation that bonds or unbonds a validator
|
||||
// depending on current status.
|
||||
// nolint: unparam
|
||||
func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var msg string
|
||||
var newStatus sdk.BondStatus
|
||||
var (
|
||||
msg string
|
||||
newStatus sdk.BondStatus
|
||||
)
|
||||
|
||||
if val.Status() == sdk.Bonded {
|
||||
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
|
@ -45,21 +46,27 @@ func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, in
|
|||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Bonded
|
||||
}
|
||||
|
||||
val, pool = val.UpdateStatus(pool, newStatus)
|
||||
return pool, val, 0, msg
|
||||
}
|
||||
|
||||
// operation: add a random number of tokens to a validator
|
||||
// OpAddTokens implements an operation that adds a random number of tokens to a
|
||||
// validator.
|
||||
func OpAddTokens(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
tokens := int64(r.Int31n(1000))
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
|
||||
tokens := int64(r.Int31n(1000))
|
||||
val, pool, _ = val.AddTokensFromDel(pool, tokens)
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
return pool, val, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||
|
||||
// Tokens are removed so for accounting must be negative
|
||||
return pool, val, -1 * tokens, msg
|
||||
}
|
||||
|
||||
// operation: remove a random number of shares from a validator
|
||||
// OpRemoveShares implements an operation that removes a random number of
|
||||
// shares from a validator.
|
||||
func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var shares sdk.Rat
|
||||
for {
|
||||
|
@ -76,7 +83,7 @@ func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, in
|
|||
return pool, val, tokens, msg
|
||||
}
|
||||
|
||||
// pick a random staking operation
|
||||
// RandomOperation returns a random staking operation.
|
||||
func RandomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
OpBondOrUnbond,
|
||||
|
@ -86,10 +93,11 @@ func RandomOperation(r *rand.Rand) Operation {
|
|||
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
|
||||
// AssertInvariants ensures invariants that should always be true are true.
|
||||
// nolint: unparam
|
||||
func AssertInvariants(t *testing.T, msg string,
|
||||
pOrig Pool, cOrig []Validator, pMod Pool, vMods []Validator, tokens int64) {
|
||||
|
@ -105,29 +113,28 @@ func AssertInvariants(t *testing.T, msg string,
|
|||
pOrig.UnbondedTokens, pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens, pMod.BondedTokens, tokens)
|
||||
|
||||
// nonnegative bonded shares
|
||||
// Nonnegative bonded shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative unbonded shares
|
||||
// Nonnegative unbonded shares
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative bonded ex rate
|
||||
// Nonnegative bonded ex rate
|
||||
require.False(t, pMod.BondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative BondedShareExRate: %d",
|
||||
msg, pMod.BondedShareExRate().RoundInt64())
|
||||
|
||||
// nonnegative unbonded ex rate
|
||||
// Nonnegative unbonded ex rate
|
||||
require.False(t, pMod.UnbondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative UnbondedShareExRate: %d",
|
||||
msg, pMod.UnbondedShareExRate().RoundInt64())
|
||||
|
||||
for _, vMod := range vMods {
|
||||
|
||||
// nonnegative ex rate
|
||||
// Nonnegative ex rate
|
||||
require.False(t, vMod.DelegatorShareExRate(pMod).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
||||
msg,
|
||||
|
@ -135,7 +142,7 @@ func AssertInvariants(t *testing.T, msg string,
|
|||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative poolShares
|
||||
// Nonnegative poolShares
|
||||
require.False(t, vMod.PoolShares.Bonded().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.PoolShares.Bonded(): %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
|
@ -145,7 +152,7 @@ func AssertInvariants(t *testing.T, msg string,
|
|||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative delShares
|
||||
// Nonnegative delShares
|
||||
require.False(t, vMod.DelegatorShares.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %v (validator.PoolShares.Bonded(): %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
|
@ -154,27 +161,25 @@ func AssertInvariants(t *testing.T, msg string,
|
|||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
// TODO refactor this random setup
|
||||
// TODO: refactor this random setup
|
||||
|
||||
// generate a random validator
|
||||
// randomValidator generates a random validator.
|
||||
// nolint: unparam
|
||||
func randomValidator(r *rand.Rand, i int) Validator {
|
||||
|
||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
|
||||
var pShares PoolShares
|
||||
|
||||
if r.Float64() < float64(0.5) {
|
||||
pShares = NewBondedShares(poolSharesAmt)
|
||||
} else {
|
||||
pShares = NewUnbondedShares(poolSharesAmt)
|
||||
}
|
||||
|
||||
return Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
|
@ -183,7 +188,7 @@ func randomValidator(r *rand.Rand, i int) Validator {
|
|||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
// RandomSetup generates a random staking state.
|
||||
func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 100000
|
||||
|
@ -191,6 +196,7 @@ func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
|||
validators := make([]Validator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validator := randomValidator(r, i)
|
||||
|
||||
if validator.Status() == sdk.Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
||||
pool.BondedTokens += validator.PoolShares.Bonded().RoundInt64()
|
||||
|
@ -198,7 +204,9 @@ func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
|||
pool.UnbondedShares = pool.UnbondedShares.Add(validator.PoolShares.Unbonded())
|
||||
pool.UnbondedTokens += validator.PoolShares.Unbonded().RoundInt64()
|
||||
}
|
||||
|
||||
validators[i] = validator
|
||||
}
|
||||
|
||||
return pool, validators
|
||||
}
|
|
@ -4,13 +4,14 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const doNotModifyDescVal = "[do-not-modify]"
|
||||
|
||||
// Validator defines the total amount of bond shares and their exchange rate to
|
||||
// coins. Accumulation of interest is modelled as an in increase in the
|
||||
// exchange rate, and slashing as a decrease. When coins are delegated to this
|
||||
|
@ -60,15 +61,13 @@ func NewValidator(owner sdk.Address, pubKey crypto.PubKey, description Descripti
|
|||
}
|
||||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
// Equal returns a boolean reflecting if two given validators are identical.
|
||||
func (v Validator) Equal(c2 Validator) bool {
|
||||
return v.PubKey.Equals(c2.PubKey) &&
|
||||
bytes.Equal(v.Owner, c2.Owner) &&
|
||||
v.PoolShares.Equal(c2.PoolShares) &&
|
||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||
v.Description == c2.Description &&
|
||||
//v.BondHeight == c2.BondHeight &&
|
||||
//v.BondIntraTxCounter == c2.BondIntraTxCounter && // counter is always changing
|
||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||
v.Commission.Equal(c2.Commission) &&
|
||||
v.CommissionMax.Equal(c2.CommissionMax) &&
|
||||
|
@ -85,6 +84,7 @@ type Description struct {
|
|||
Details string `json:"details"` // optional details
|
||||
}
|
||||
|
||||
// NewDescription returns a new Description with the provided values.
|
||||
func NewDescription(moniker, identity, website, details string) Description {
|
||||
return Description{
|
||||
Moniker: moniker,
|
||||
|
@ -94,20 +94,22 @@ func NewDescription(moniker, identity, website, details string) Description {
|
|||
}
|
||||
}
|
||||
|
||||
// update the description based on input
|
||||
// UpdateDescription updates the fields of a given description. An error is
|
||||
// returned if the resulting description contains an invalid length.
|
||||
func (d Description) UpdateDescription(d2 Description) (Description, sdk.Error) {
|
||||
if d.Moniker == "[do-not-modify]" {
|
||||
if d.Moniker == doNotModifyDescVal {
|
||||
d2.Moniker = d.Moniker
|
||||
}
|
||||
if d.Identity == "[do-not-modify]" {
|
||||
if d.Identity == doNotModifyDescVal {
|
||||
d2.Identity = d.Identity
|
||||
}
|
||||
if d.Website == "[do-not-modify]" {
|
||||
if d.Website == doNotModifyDescVal {
|
||||
d2.Website = d.Website
|
||||
}
|
||||
if d.Details == "[do-not-modify]" {
|
||||
if d.Details == doNotModifyDescVal {
|
||||
d2.Details = d.Details
|
||||
}
|
||||
|
||||
return Description{
|
||||
Moniker: d2.Moniker,
|
||||
Identity: d2.Identity,
|
||||
|
@ -116,7 +118,7 @@ func (d Description) UpdateDescription(d2 Description) (Description, sdk.Error)
|
|||
}.EnsureLength()
|
||||
}
|
||||
|
||||
// ensure the length of the description
|
||||
// EnsureLength ensures the length of a validator's description.
|
||||
func (d Description) EnsureLength() (Description, sdk.Error) {
|
||||
if len(d.Moniker) > 70 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "moniker", len(d.Moniker), 70)
|
||||
|
@ -130,10 +132,11 @@ func (d Description) EnsureLength() (Description, sdk.Error) {
|
|||
if len(d.Details) > 280 {
|
||||
return d, ErrDescriptionLength(DefaultCodespace, "details", len(d.Details), 280)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
// ABCIValidator returns an abci.Validator from a staked validator type.
|
||||
func (v Validator) ABCIValidator() abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
||||
|
@ -141,8 +144,8 @@ func (v Validator) ABCIValidator() abci.Validator {
|
|||
}
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
// with zero power used for validator updates
|
||||
// ABCIValidatorZero returns an abci.Validator from a staked validator type
|
||||
// with with zero power used for validator updates.
|
||||
func (v Validator) ABCIValidatorZero() abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
||||
|
@ -150,12 +153,13 @@ func (v Validator) ABCIValidatorZero() abci.Validator {
|
|||
}
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
// Status returns the validator's bond status inferred from the pool shares.
|
||||
func (v Validator) Status() sdk.BondStatus {
|
||||
return v.PoolShares.Status
|
||||
}
|
||||
|
||||
// update the location of the shares within a validator if its bond status has changed
|
||||
// UpdateStatus updates the location of the shares within a validator if it's
|
||||
// bond status has changed.
|
||||
func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) {
|
||||
var tokens int64
|
||||
|
||||
|
@ -173,7 +177,8 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
|
|||
pool, tokens = pool.removeSharesUnbonding(v.PoolShares.Amount)
|
||||
|
||||
case sdk.Bonded:
|
||||
if NewStatus == sdk.Bonded { // return if nothing needs switching
|
||||
if NewStatus == sdk.Bonded {
|
||||
// Return if nothing needs switching
|
||||
return v, pool
|
||||
}
|
||||
pool, tokens = pool.removeSharesBonded(v.PoolShares.Amount)
|
||||
|
@ -187,14 +192,16 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator,
|
|||
case sdk.Bonded:
|
||||
pool, v.PoolShares = pool.addTokensBonded(tokens)
|
||||
}
|
||||
|
||||
return v, pool
|
||||
}
|
||||
|
||||
// Remove pool shares
|
||||
// Returns corresponding tokens, which could be burned (e.g. when slashing
|
||||
// a validator) or redistributed elsewhere
|
||||
// RemovePoolShares removes pool shares from a validator. It returns
|
||||
// corresponding tokens, which could be burned (e.g. when slashing a validator)
|
||||
// or redistributed elsewhere.
|
||||
func (v Validator) RemovePoolShares(pool Pool, poolShares sdk.Rat) (Validator, Pool, int64) {
|
||||
var tokens int64
|
||||
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
pool, tokens = pool.removeSharesUnbonded(poolShares)
|
||||
|
@ -203,29 +210,33 @@ func (v Validator) RemovePoolShares(pool Pool, poolShares sdk.Rat) (Validator, P
|
|||
case sdk.Bonded:
|
||||
pool, tokens = pool.removeSharesBonded(poolShares)
|
||||
}
|
||||
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(poolShares)
|
||||
return v, pool, tokens
|
||||
}
|
||||
|
||||
// TODO remove should only be tokens
|
||||
// get the power or potential power for a validator
|
||||
// if bonded, the power is the BondedShares
|
||||
// if not bonded, the power is the amount of bonded shares which the
|
||||
// the validator would have it was bonded
|
||||
// EquivalentBondedShares ...
|
||||
//
|
||||
// TODO: Remove should only be tokens get the power or potential power for a
|
||||
// validator if bonded, the power is the BondedShares if not bonded, the power
|
||||
// is the amount of bonded shares which the the validator would have it was
|
||||
// bonded.
|
||||
func (v Validator) EquivalentBondedShares(pool Pool) (eqBondedShares sdk.Rat) {
|
||||
return v.PoolShares.ToBonded(pool).Amount
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// add tokens to a validator
|
||||
func (v Validator) AddTokensFromDel(pool Pool,
|
||||
amount int64) (validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat) {
|
||||
// AddTokensFromDel adds tokens to a validator
|
||||
func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Rat) {
|
||||
var (
|
||||
poolShares PoolShares
|
||||
equivalentBondedShares sdk.Rat
|
||||
)
|
||||
|
||||
exRate := v.DelegatorShareExRate(pool) // bshr/delshr
|
||||
// bondedShare/delegatedShare
|
||||
exRate := v.DelegatorShareExRate(pool)
|
||||
|
||||
var poolShares PoolShares
|
||||
var equivalentBondedShares sdk.Rat
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
pool, poolShares = pool.addTokensUnbonded(amount)
|
||||
|
@ -237,21 +248,24 @@ func (v Validator) AddTokensFromDel(pool Pool,
|
|||
v.PoolShares.Amount = v.PoolShares.Amount.Add(poolShares.Amount)
|
||||
equivalentBondedShares = poolShares.ToBonded(pool).Amount
|
||||
|
||||
issuedDelegatorShares = equivalentBondedShares.Quo(exRate) // bshr/(bshr/delshr) = delshr
|
||||
// bondedShare/(bondedShare/delegatedShare) = delegatedShare
|
||||
issuedDelegatorShares := equivalentBondedShares.Quo(exRate)
|
||||
v.DelegatorShares = v.DelegatorShares.Add(issuedDelegatorShares)
|
||||
|
||||
return v, pool, issuedDelegatorShares
|
||||
}
|
||||
|
||||
// remove delegator shares from a validator
|
||||
// NOTE this function assumes the shares have already been updated for the validator status
|
||||
func (v Validator) RemoveDelShares(pool Pool,
|
||||
delShares sdk.Rat) (validator2 Validator, p2 Pool, createdCoins int64) {
|
||||
|
||||
// RemoveDelShares removes delegator shares from a validator.
|
||||
//
|
||||
// NOTE: This function assumes the shares have already been updated for the
|
||||
// validator status.
|
||||
func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Pool, int64) {
|
||||
amount := v.DelegatorShareExRate(pool).Mul(delShares)
|
||||
eqBondedSharesToRemove := NewBondedShares(amount)
|
||||
v.DelegatorShares = v.DelegatorShares.Sub(delShares)
|
||||
|
||||
var createdCoins int64
|
||||
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
unbondedShares := eqBondedSharesToRemove.ToUnbonded(pool).Amount
|
||||
|
@ -265,15 +279,17 @@ func (v Validator) RemoveDelShares(pool Pool,
|
|||
pool, createdCoins = pool.removeSharesBonded(eqBondedSharesToRemove.Amount)
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(eqBondedSharesToRemove.Amount)
|
||||
}
|
||||
|
||||
return v, pool, createdCoins
|
||||
}
|
||||
|
||||
// get the exchange rate of tokens over delegator shares
|
||||
// DelegatorShareExRate gets the exchange rate of tokens over delegator shares.
|
||||
// UNITS: eq-val-bonded-shares/delegator-shares
|
||||
func (v Validator) DelegatorShareExRate(pool Pool) sdk.Rat {
|
||||
if v.DelegatorShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
|
||||
eqBondedShares := v.PoolShares.ToBonded(pool).Amount
|
||||
return eqBondedShares.Quo(v.DelegatorShares)
|
||||
}
|
||||
|
@ -293,16 +309,20 @@ func (v Validator) GetPower() sdk.Rat { return v.PoolShares.Bonded() }
|
|||
func (v Validator) GetDelegatorShares() sdk.Rat { return v.DelegatorShares }
|
||||
func (v Validator) GetBondHeight() int64 { return v.BondHeight }
|
||||
|
||||
//Human Friendly pretty printer
|
||||
// HumanReadableString returns a human readable string representation of a
|
||||
// validator. An error is returned if the owner or the owner's public key
|
||||
// cannot be converted to Bech32 format.
|
||||
func (v Validator) HumanReadableString() (string, error) {
|
||||
bechOwner, err := sdk.Bech32ifyAcc(v.Owner)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bechVal, err := sdk.Bech32ifyValPub(v.PubKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp := "Validator \n"
|
||||
resp += fmt.Sprintf("Owner: %s\n", bechOwner)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
|
|
|
@ -5,12 +5,89 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestValidatorEqual(t *testing.T) {
|
||||
val1 := NewValidator(addr1, pk1, Description{})
|
||||
val2 := NewValidator(addr1, pk1, Description{})
|
||||
|
||||
ok := val1.Equal(val2)
|
||||
require.True(t, ok)
|
||||
|
||||
val2 = NewValidator(addr2, pk2, Description{})
|
||||
|
||||
ok = val1.Equal(val2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestUpdateDescription(t *testing.T) {
|
||||
d1 := Description{
|
||||
Moniker: doNotModifyDescVal,
|
||||
Identity: doNotModifyDescVal,
|
||||
Website: doNotModifyDescVal,
|
||||
Details: doNotModifyDescVal,
|
||||
}
|
||||
d2 := Description{
|
||||
Website: "https://validator.cosmos",
|
||||
Details: "Test validator",
|
||||
}
|
||||
|
||||
d, err := d1.UpdateDescription(d2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, d, d1)
|
||||
}
|
||||
|
||||
func TestABCIValidator(t *testing.T) {
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
|
||||
abciVal := val.ABCIValidator()
|
||||
require.Equal(t, tmtypes.TM2PB.PubKey(val.PubKey), abciVal.PubKey)
|
||||
require.Equal(t, val.PoolShares.Bonded().RoundInt64(), abciVal.Power)
|
||||
}
|
||||
|
||||
func TestABCIValidatorZero(t *testing.T) {
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
|
||||
abciVal := val.ABCIValidatorZero()
|
||||
require.Equal(t, tmtypes.TM2PB.PubKey(val.PubKey), abciVal.PubKey)
|
||||
require.Equal(t, int64(0), abciVal.Power)
|
||||
}
|
||||
|
||||
func TestRemovePoolShares(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
|
||||
val := Validator{
|
||||
Owner: addr1,
|
||||
PubKey: pk1,
|
||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
||||
DelegatorShares: sdk.NewRat(100),
|
||||
}
|
||||
|
||||
pool.BondedTokens = val.PoolShares.Bonded().RoundInt64()
|
||||
pool.BondedShares = val.PoolShares.Bonded()
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
val, pool, tk := val.RemovePoolShares(pool, sdk.NewRat(10))
|
||||
require.Equal(t, int64(90), val.PoolShares.Amount.RoundInt64())
|
||||
require.Equal(t, int64(90), pool.BondedTokens)
|
||||
require.Equal(t, int64(90), pool.BondedShares.RoundInt64())
|
||||
require.Equal(t, int64(20), pool.LooseTokens)
|
||||
require.Equal(t, int64(10), tk)
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
||||
val, pool, tk = val.RemovePoolShares(pool, sdk.NewRat(10))
|
||||
require.Equal(t, int64(80), val.PoolShares.Amount.RoundInt64())
|
||||
require.Equal(t, int64(0), pool.BondedTokens)
|
||||
require.Equal(t, int64(0), pool.BondedShares.RoundInt64())
|
||||
require.Equal(t, int64(30), pool.LooseTokens)
|
||||
require.Equal(t, int64(10), tk)
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorBonded(t *testing.T) {
|
||||
pool := InitialPool()
|
||||
pool.LooseTokens = 10
|
||||
|
@ -230,3 +307,13 @@ func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHumanReadableString(t *testing.T) {
|
||||
val := NewValidator(addr1, pk1, Description{})
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
valStr, err := val.HumanReadableString()
|
||||
require.Nil(t, err)
|
||||
require.NotEmpty(t, valStr)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue