Merge branch 'develop' into develop

This commit is contained in:
Rigel 2018-06-27 19:22:48 -04:00 committed by GitHub
commit 50733f5c22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
121 changed files with 6986 additions and 4359 deletions

View File

@ -49,7 +49,7 @@ jobs:
lint:
<<: *defaults
parallelism: 4
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
@ -61,17 +61,17 @@ jobs:
name: Get metalinter
command: |
export PATH="$GOBIN:$PATH"
go get -u github.com/tendermint/lint/golint
go get -u github.com/alecthomas/gometalinter
make get_tools
go get -u github.com/client9/misspell/cmd/misspell
- run:
name: Lint source
command: |
export PATH="$GOBIN:$PATH"
gometalinter --disable-all --enable='golint' --vendor ./...
gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --vendor ./...
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
test_unit:
<<: *defaults
parallelism: 4
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
@ -84,7 +84,7 @@ jobs:
command: |
export PATH="$GOBIN:$PATH"
make test_unit
test_cli:
<<: *defaults
parallelism: 1
@ -100,10 +100,10 @@ jobs:
command: |
export PATH="$GOBIN:$PATH"
make test_cli_retry
test_cover:
<<: *defaults
parallelism: 4
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace

View File

@ -9,6 +9,17 @@ BREAKING CHANGES
* AltBytes renamed to Memo, now a string, max 100 characters, costs a bit of gas
* Transactions now take a list of Messages
* Signers of a transaction now only sign over their account and sequence number
* Removed MsgChangePubKey from auth
* Removed setPubKey from account mapper
* Removed GetMemo from Tx (it is still on StdTx)
* [cli] rearranged commands under subcommands
* [stake] remove Tick and add EndBlocker
* [stake] introduce concept of unbonding for delegations and validators
* `gaiacli stake unbond` replaced with `gaiacli stake begin-unbonding`
* introduced:
* `gaiacli stake complete-unbonding`
* `gaiacli stake begin-redelegation`
* `gaiacli stake complete-redelegation`
FEATURES
* [gaiacli] You can now attach a simple text-only memo to any transaction, with the `--memo` flag
@ -18,11 +29,41 @@ FEATURES
* Supported proposal types: just binary (pass/fail) TextProposals for now
* Proposals need deposits to be votable; deposits are burned if proposal fails
* Delegators delegate votes to validator by default but can override (for their stake)
* Add benchmarks for signing and delivering a block with a single bank transaction
* Run with `cd x/bank && go test --bench=.`
* [tools] make get_tools installs tendermint's linter, and gometalinter
* [tools] Switch gometalinter to the stable version
* [tools] Add checking for misspellings and for incorrectly formatted files in circle CI
* [server] Default config now creates a profiler at port 6060, and increase p2p send/recv rates
* [tests] Add WaitForNextNBlocksTM helper method
* [types] Switches internal representation of Int/Uint/Rat to use pointers
* [gaiad] unsafe_reset_all now resets addrbook.json
* [democoin] add x/oracle, x/assoc
FIXES
FIXES
* [gaia] Added self delegation for validators in the genesis creation
* [lcd] tests now don't depend on raw json text
* [stake] error strings lower case
* [stake] pool loose tokens now accounts for unbonding and unbonding tokens not associated with any validator
* \#1259 - fix bug where certain tests that could have a nil pointer in defer
* \#1052 - Make all now works
* Retry on HTTP request failure in CLI tests, add option to retry tests in Makefile
* Fixed bug where chain ID wasn't passed properly in x/bank REST handler
* Fixed bug where chain ID wasn't passed properly in x/bank REST handler, removed Viper hack from ante handler
* Fixed bug where `democli account` didn't decode the account data correctly
* \#1343 - fixed unnecessary parallelism in CI
* \#1367 - set ChainID in InitChain
* \#1353 - CLI: Show pool shares fractions in human-readable format
* \#1258 - printing big.rat's can no longer overflow int64
IMPROVEMENTS
* bank module uses go-wire codec instead of 'encoding/json'
* auth module uses go-wire codec instead of 'encoding/json'
* revised use of endblock and beginblock
* [stake] module reorganized to include `types` and `keeper` package
* [stake] keeper always loads the store (instead passing around which doesn't really boost efficiency)
* [stake] edit-validator changes now can use the keyword [do-not-modify] to not modify unspecified `--flag` (aka won't set them to `""` value)
* [types] added common tag constants
* [stake] offload more generic functionality from the handler into the keeper
## 0.19.0

12
Gopkg.lock generated
View File

@ -256,7 +256,7 @@
"leveldb/table",
"leveldb/util"
]
revision = "e2150783cd35f5b607daca48afd8c57ec54cc995"
revision = "0d5a0ceb10cf9ab89fdd744cc8c50a83134f6697"
[[projects]]
name = "github.com/tendermint/abci"
@ -350,7 +350,6 @@
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
[[projects]]
branch = "develop"
name = "github.com/tendermint/tmlibs"
packages = [
"autofile",
@ -373,6 +372,7 @@
packages = [
"blowfish",
"curve25519",
"internal/subtle",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
@ -381,7 +381,7 @@
"ripemd160",
"salsa20/salsa"
]
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
[[projects]]
branch = "master"
@ -395,13 +395,13 @@
"internal/timeseries",
"trace"
]
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "bff228c7b664c5fce602223a05fb708fd8654986"
revision = "a200a19cb90b19de298170992778b1fda7217bd6"
[[projects]]
name = "golang.org/x/text"
@ -462,6 +462,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d02a24bcfd8bded901e1b154e19b81ff797d3921046ede19d1d11eed61e871e7"
inputs-digest = "8ad5e4b90e57805024944a6ee5eed246f109c18724d453093e82bac1469dbd9e"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -58,7 +58,7 @@
[[constraint]]
name = "github.com/tendermint/go-crypto"
version = "~0.6.2"
version = "=0.6.2"
[[constraint]]
name = "github.com/tendermint/go-amino"
@ -66,7 +66,7 @@
[[constraint]]
name = "github.com/tendermint/iavl"
version = "0.8.0-rc0"
version = "=0.8.0-rc0"
[[constraint]]
name = "github.com/tendermint/tendermint"
@ -74,7 +74,7 @@
[[override]]
name = "github.com/tendermint/tmlibs"
branch = "develop"
revision = "0c98d10b4ffbd87978d79c160e835b3d3df241ec"
# this got updated and broke, so locked to an old working commit ...
[[override]]

View File

@ -3,7 +3,7 @@ PACKAGES_NOCLITEST=$(shell go list ./... | grep -v '/vendor/' | grep -v github.c
COMMIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
all: check_tools get_vendor_deps install install_examples test_lint test
all: get_tools get_vendor_deps install install_examples test_lint test
########################################
### CI
@ -36,11 +36,11 @@ else
go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli
endif
install:
install:
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
install_examples:
install_examples:
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
@ -89,7 +89,7 @@ godocs:
test: test_unit
test_cli:
test_cli:
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test`
test_cli_retry:
@ -108,7 +108,7 @@ test_cover:
@bash tests/test_cover.sh
test_lint:
gometalinter --disable-all --enable='golint' --vendor ./...
gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --vendor ./...
benchmark:
@go test -bench=. $(PACKAGES_NOCLITEST)

View File

@ -179,7 +179,7 @@ func (app *BaseApp) LastCommitID() sdk.CommitID {
return app.cms.LastCommitID()
}
// the last commited block height
// the last committed block height
func (app *BaseApp) LastBlockHeight() int64 {
return app.cms.LastCommitID().Version
}
@ -224,9 +224,6 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
}
*/
// initialize Check state
app.setCheckState(abci.Header{})
return nil
}
@ -287,12 +284,13 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
// Implements ABCI
// InitChain runs the initialization logic directly on the CommitMultiStore and commits it.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// Initialize the deliver state and check state with ChainID and run initChain
app.setDeliverState(abci.Header{ChainID: req.ChainId})
app.setCheckState(abci.Header{ChainID: req.ChainId})
if app.initChainer == nil {
return
}
// Initialize the deliver state and run initChain
app.setDeliverState(abci.Header{})
app.initChainer(app.deliverState.ctx, req) // no error
// NOTE: we don't commit, but BeginBlock for block 1
@ -379,10 +377,15 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
// Initialize the DeliverTx state.
// If this is the first block, it should already
// be initialized in InitChain. It may also be nil
// if this is a test and InitChain was never called.
// be initialized in InitChain.
// Otherwise app.deliverState will be nil, since it
// is reset on Commit.
if app.deliverState == nil {
app.setDeliverState(req.Header)
} else {
// In the first block, app.deliverState.ctx will already be initialized
// by InitChain. Context is now updated with Header information.
app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header)
}
if app.beginBlocker != nil {
res = app.beginBlocker(app.deliverState.ctx, req)
@ -598,6 +601,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// Write the Deliver state and commit the MultiStore
app.deliverState.ms.Write()
commitID := app.cms.Commit()
// TODO: this is missing a module identifier and dumps byte array
app.Logger.Debug("Commit synced",
"commit", commitID,
)

View File

@ -3,7 +3,6 @@ package baseapp
import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/x/bank"
"os"
"testing"
@ -20,6 +19,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
)
func defaultLogger() log.Logger {
@ -202,7 +202,15 @@ func TestInitChainer(t *testing.T) {
// set initChainer and try again - should see the value
app.SetInitChainer(initChainer)
app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}")}) // must have valid JSON genesis file, even if empty
app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty
// assert that chainID is set correctly in InitChain
chainID := app.deliverState.ctx.ChainID()
assert.Equal(t, "test-chain-id", chainID, "ChainID in deliverState not set correctly in InitChain")
chainID = app.checkState.ctx.ChainID()
assert.Equal(t, "test-chain-id", chainID, "ChainID in checkState not set correctly in InitChain")
app.Commit()
res = app.Query(query)
assert.Equal(t, value, res.Value)
@ -378,13 +386,15 @@ func TestSimulateTx(t *testing.T) {
return ttx, nil
})
app.InitChain(abci.RequestInitChain{})
nBlocks := 3
for blockN := 0; blockN < nBlocks; blockN++ {
// block1
header.Height = int64(blockN + 1)
app.BeginBlock(abci.RequestBeginBlock{Header: header})
result := app.Simulate(tx)
require.Equal(t, result.Code, sdk.ABCICodeOK)
require.Equal(t, result.Code, sdk.ABCICodeOK, result.Log)
require.Equal(t, int64(80), result.GasUsed)
counter--
encoded, err := json.Marshal(tx)
@ -397,8 +407,8 @@ func TestSimulateTx(t *testing.T) {
require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK))
var res sdk.Result
app.cdc.MustUnmarshalBinary(queryResult.Value, &res)
require.Equal(t, sdk.ABCICodeOK, res.Code)
require.Equal(t, int64(160), res.GasUsed)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
require.Equal(t, int64(160), res.GasUsed, res.Log)
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
}

View File

@ -32,7 +32,7 @@ import (
func TestKeys(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKB(t))
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
defer cleanup()
// get seed
@ -218,7 +218,7 @@ func TestValidators(t *testing.T) {
func TestCoinSend(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKB(t))
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
defer cleanup()
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
@ -236,7 +236,7 @@ func TestCoinSend(t *testing.T) {
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -260,7 +260,7 @@ func TestCoinSend(t *testing.T) {
func TestIBCTransfer(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKB(t))
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
defer cleanup()
acc := getAccount(t, port, addr)
@ -271,7 +271,7 @@ func TestIBCTransfer(t *testing.T) {
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -289,7 +289,7 @@ func TestIBCTransfer(t *testing.T) {
func TestTxs(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKB(t))
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
defer cleanup()
// query wrong
@ -378,16 +378,16 @@ func TestValidatorsQuery(t *testing.T) {
func TestBonding(t *testing.T) {
name, password, denom := "test", "1234567890", "steak"
addr, seed := CreateAddr(t, "test", password, GetKB(t))
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.Address{addr})
defer cleanup()
validator1Owner := pks[0].Address()
// create bond TX
resultTx := doBond(t, port, seed, name, password, addr, validator1Owner)
resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -405,23 +405,24 @@ func TestBonding(t *testing.T) {
// testing unbonding
// create unbond TX
resultTx = doUnbond(t, port, seed, name, password, addr, validator1Owner)
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner)
tests.WaitForHeight(resultTx.Height+1, port)
// query validator
bond = getDelegation(t, port, addr, validator1Owner)
assert.Equal(t, "30/1", bond.Shares.String())
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// TODO fix shares fn in staking
// should the sender should have not received any coins as the unbonding has only just begun
// query sender
//acc := getAccount(t, sendAddr)
//coins := acc.GetCoins()
//assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
acc = getAccount(t, port, addr)
coins = acc.GetCoins()
assert.Equal(t, int64(40), coins.AmountOf("steak").Int64())
// TODO add redelegation, need more complex capabilities such to mock context and
}
func TestSubmitProposal(t *testing.T) {
@ -434,7 +435,7 @@ func TestSubmitProposal(t *testing.T) {
resultTx := doSubmitProposal(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -456,7 +457,7 @@ func TestDeposit(t *testing.T) {
resultTx := doSubmitProposal(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -486,7 +487,7 @@ func TestVote(t *testing.T) {
resultTx := doSubmitProposal(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
// check if tx was committed
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
@ -572,6 +573,8 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Add
receiveAddr := receiveInfo.PubKey.Address()
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
chainID := viper.GetString(client.FlagChainID)
// get the account to get the sequence
acc := getAccount(t, port, addr)
accnum := acc.GetAccountNumber()
@ -584,13 +587,14 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Add
"account_number":%d,
"sequence": %d,
"gas": 100000,
"chain_id": "%s",
"amount":[
{
"denom": "%s",
"amount": 1
}
]
}`, name, password, accnum, sequence, "steak"))
}`, name, password, accnum, sequence, chainID, "steak"))
res, body := Request(t, port, "POST", "/ibc/testchain/"+receiveAddrBech+"/send", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -606,7 +610,7 @@ func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.A
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
// get the account to get the sequence
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/bonding_status/"+validatorAddrBech, nil)
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/delegation/"+validatorAddrBech, nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var bond stake.Delegation
err := cdc.UnmarshalJSON([]byte(body), &bond)
@ -614,7 +618,7 @@ func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.A
return bond
}
func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
@ -623,6 +627,8 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
@ -630,15 +636,19 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
"account_number": %d,
"sequence": %d,
"gas": 10000,
"delegate": [
"chain_id": "%s",
"delegations": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"bond": { "denom": "%s", "amount": 60 }
}
],
"unbond": []
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech, "steak"))
"begin_unbondings": [],
"complete_unbondings": [],
"begin_redelegates": [],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorAddrBech, "steak"))
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -649,7 +659,9 @@ func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, vali
return results[0]
}
func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
func doBeginUnbonding(t *testing.T, port, seed, name, password string,
delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
@ -658,6 +670,8 @@ func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, va
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
@ -665,15 +679,64 @@ func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, va
"account_number": %d,
"sequence": %d,
"gas": 10000,
"delegate": [],
"unbond": [
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"shares": "30"
}
]
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech))
],
"complete_unbondings": [],
"begin_redelegates": [],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorAddrBech))
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func doBeginRedelegation(t *testing.T, port, seed, name, password string,
delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
validatorSrcAddrBech := sdk.MustBech32ifyVal(validatorSrcAddr)
validatorDstAddrBech := sdk.MustBech32ifyVal(validatorDstAddr)
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": %d,
"sequence": %d,
"gas": 10000,
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [],
"complete_unbondings": [],
"begin_redelegates": [
{
"delegator_addr": "%s",
"validator_src_addr": "%s",
"validator_dst_addr": "%s",
"shares": "30"
}
],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddrBech, validatorSrcAddrBech, validatorDstAddrBech))
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)

View File

@ -100,13 +100,13 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
config.TxIndex.IndexAllTags = true
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
logger = log.NewFilter(logger, log.AllowError())
logger = log.NewFilter(logger, log.AllowDebug())
privValidatorFile := config.PrivValidatorFile()
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
privVal.Reset()
db := dbm.NewMemDB()
app := gapp.NewGaiaApp(logger, db)
cdc = gapp.MakeCodec() // XXX
cdc = gapp.MakeCodec()
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
@ -146,6 +146,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
acc := gapp.NewGenesisAccount(&accAuth)
genesisState.Accounts = append(genesisState.Accounts, acc)
genesisState.StakeData.Pool.LooseTokens += 100
}
appState, err := wire.MarshalJSONIndent(cdc, genesisState)

View File

@ -3,6 +3,7 @@ package app
import (
"encoding/json"
"errors"
"github.com/spf13/pflag"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
@ -17,8 +18,8 @@ import (
var (
// bonded tokens given to genesis validators/accounts
freeFermionVal = sdk.NewInt(100)
freeFermionsAcc = sdk.NewInt(50)
freeFermionVal = int64(100)
freeFermionsAcc = int64(50)
)
// State to Unmarshal
@ -124,7 +125,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.Address, name st
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: freeFermionVal.Int64(),
Power: freeFermionVal,
}
return
}
@ -155,22 +156,33 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
accAuth.Coins = sdk.Coins{
{genTx.Name + "Token", sdk.NewInt(1000)},
{"steak", freeFermionsAcc},
{"steak", sdk.NewInt(freeFermionsAcc)},
}
acc := NewGenesisAccount(&accAuth)
genaccs[i] = acc
stakeData.Pool.LooseUnbondedTokens = stakeData.Pool.LooseUnbondedTokens.Add(freeFermionsAcc) // increase the supply
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionsAcc // increase the supply
// add the validator
if len(genTx.Name) > 0 {
desc := stake.NewDescription(genTx.Name, "", "", "")
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
validator.PoolShares = stake.NewBondedShares(sdk.NewRatFromInt(freeFermionVal))
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionVal // increase the supply
// add some new shares to the validator
var issuedDelShares sdk.Rat
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, freeFermionVal)
stakeData.Validators = append(stakeData.Validators, validator)
// pool logic
stakeData.Pool.BondedTokens = stakeData.Pool.BondedTokens.Add(freeFermionVal)
stakeData.Pool.BondedShares = sdk.NewRatFromInt(stakeData.Pool.BondedTokens)
// create the self-delegation from the issuedDelShares
delegation := stake.Delegation{
DelegatorAddr: validator.Owner,
ValidatorAddr: validator.Owner,
Shares: issuedDelShares,
Height: 0,
}
stakeData.Bonds = append(stakeData.Bonds, delegation)
}
}

View File

@ -95,7 +95,8 @@ func main() {
stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
stakecmd.GetCmdUnbond("stake", cdc),
stakecmd.GetCmdRedelegate("stake", cdc),
slashingcmd.GetCmdUnrevoke(cdc),
)...)
rootCmd.AddCommand(

View File

@ -110,7 +110,7 @@ Your node needs to know how to find peers. You'll need to add healthy seed nodes
```toml
# Comma separated list of seed nodes to connect to
seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6002.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656"
seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6002.coinculture.net:46656,1e124dd15bd9955a7ea844ab003b1b47f0998b70@seed.cosmos.cryptium.ch:46656"
```
If those seeds aren't working, you can find more seeds and persistent peers on the [Cosmos Explorer](https://explorecosmos.network/nodes). Open the the `Full Nodes` pane and select nodes that do not have private (`10.x.x.x`) or [local IP addresses](https://en.wikipedia.org/wiki/Private_network). The `Persistent Peer` field contains the connection string. For best results use 4-6.

View File

@ -57,7 +57,7 @@ Start Nodes
Now that we've initialized the chains, we can start both nodes:
NOTE: each command below must be started in seperate terminal windows. Alternatively, to run this testnet across multiple machines, you'd replace the ``seeds = "0.0.0.0"`` in ``~/.gaia2.config.toml`` with the IP of the first node, and could skip the modifications we made to the config file above because port conflicts would be avoided.
NOTE: each command below must be started in separate terminal windows. Alternatively, to run this testnet across multiple machines, you'd replace the ``seeds = "0.0.0.0"`` in ``~/.gaia2.config.toml`` with the IP of the first node, and could skip the modifications we made to the config file above because port conflicts would be avoided.
::

25
docs/spec/other/bech32.md Normal file
View File

@ -0,0 +1,25 @@
# Bech32 on Cosmos
The Cosmos network prefers to use the Bech32 address format whereever users must handle binary data. Bech32 encoding provides robust integrity checks on data and the human readable part(HRP) provides contextual hints that can assist UI developers with providing informative error messages.
In the Cosmos network, keys and addresses may refer to a number of different roles in the network like accounts, validators etc.
## HRP table
| HRP | Definition |
| ------------- |:-------------:|
| `cosmosaccaddr` | Cosmos Account Address |
| `cosmosaccpub` | Cosmos Account Public Key |
| `cosmosvaladdr` | Cosmos Consensus Address |
| `cosmosvalpub` | Cosmos Consensus Public Key|
## Encoding
While all user facing interfaces to Cosmos software should exposed bech32 interfaces, many internal interfaces encode binary value in hex or base64 encoded form.
To covert between other binary reprsentation of addresses and keys, it is important to first apply the Amino enocoding process before bech32 encoding.
A complete implementation of the Amino serialization format is unncessary in most cases. Simply prepending bytes from this [table](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography) to the bytestring payload before bech32 encoding will sufficient for compatible representation.
 

View File

@ -37,8 +37,8 @@ processProvisions():
provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr)
pool.LooseUnbondedTokens += provisions
feePool += LooseUnbondedTokens
pool.LooseTokens += provisions
feePool += LooseTokens
setPool(pool)

View File

@ -12,7 +12,7 @@ information, etc.
```golang
type Pool struct {
LooseUnbondedTokens int64 // tokens not associated with any validator
LooseTokens int64 // tokens not associated with any validator
UnbondedTokens int64 // reserve of unbonded tokens held with validators
UnbondingTokens int64 // tokens moving from bonded to unbonded pool
BondedTokens int64 // reserve of bonded tokens

View File

@ -167,7 +167,7 @@ startUnbonding(tx TxStartUnbonding):
### TxCompleteUnbonding
Complete the unbonding and transfer the coins to the delegate. Perform any
slashing that occured during the unbonding period.
slashing that occurred during the unbonding period.
```golang
type TxUnbondingComplete struct {

View File

@ -77,7 +77,6 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// register message routes
app.Router().
AddRoute("auth", auth.NewHandler(app.accountMapper)).
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
@ -173,7 +172,8 @@ func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage,
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := types.GenesisState{
Accounts: accounts,
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
}
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {

View File

@ -2,7 +2,9 @@ package app
import (
"os"
"fmt"
"testing"
"encoding/json"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -12,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
gen "github.com/cosmos/cosmos-sdk/x/stake/types"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
@ -71,6 +74,16 @@ func TestGenesis(t *testing.T) {
// reload app and ensure the account is still there
bapp = NewBasecoinApp(logger, db)
// Initialize stake data with default genesis state
stakedata := gen.DefaultGenesisState()
genState, err := json.Marshal(stakedata)
if err != nil {
panic(err)
}
// InitChain with default stake data. Initializes deliverState and checkState context
bapp.InitChain(abci.RequestInitChain{AppStateBytes: []byte(fmt.Sprintf("{\"stake\": %s}", string(genState)))})
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)

View File

@ -51,6 +51,10 @@ func main() {
// add query/post commands (custom to binary)
rootCmd.AddCommand(
client.GetCommands(
stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
)...)
@ -62,7 +66,7 @@ func main() {
stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
stakecmd.GetCmdUnbond("stake", cdc),
)...)
// add proxy, version and key info

View File

@ -55,6 +55,7 @@ func TestGenesis(t *testing.T) {
// reload app and ensure the account is still there
bapp = NewDemocoinApp(logger, db)
bapp.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}")})
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)

View File

@ -0,0 +1,122 @@
package mock
import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/go-crypto"
)
// Validator implements sdk.Validator
type Validator struct {
Address sdk.Address
Power sdk.Rat
}
// Implements sdk.Validator
func (v Validator) GetStatus() sdk.BondStatus {
return sdk.Bonded
}
// Implements sdk.Validator
func (v Validator) GetOwner() sdk.Address {
return v.Address
}
// Implements sdk.Validator
func (v Validator) GetPubKey() crypto.PubKey {
return nil
}
// Implements sdk.Validator
func (v Validator) GetPower() sdk.Rat {
return v.Power
}
// Implements sdk.Validator
func (v Validator) GetDelegatorShares() sdk.Rat {
return sdk.ZeroRat()
}
// Implements sdk.Validator
func (v Validator) GetBondHeight() int64 {
return 0
}
// Implements sdk.Validator
func (v Validator) GetMoniker() string {
return ""
}
// Implements sdk.Validator
type ValidatorSet struct {
Validators []Validator
}
// IterateValidators implements sdk.ValidatorSet
func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
for i, val := range vs.Validators {
if fn(int64(i), val) {
break
}
}
}
// IterateValidatorsBonded implements sdk.ValidatorSet
func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
vs.IterateValidators(ctx, fn)
}
// Validator implements sdk.ValidatorSet
func (vs *ValidatorSet) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
for _, val := range vs.Validators {
if bytes.Equal(val.Address, addr) {
return val
}
}
return nil
}
// TotalPower implements sdk.ValidatorSet
func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Rat {
res := sdk.ZeroRat()
for _, val := range vs.Validators {
res = res.Add(val.Power)
}
return res
}
// Helper function for adding new validator
func (vs *ValidatorSet) AddValidator(val Validator) {
vs.Validators = append(vs.Validators, val)
}
// Helper function for removing exsting validator
func (vs *ValidatorSet) RemoveValidator(addr sdk.Address) {
pos := -1
for i, val := range vs.Validators {
if bytes.Equal(val.Address, addr) {
pos = i
break
}
}
if pos == -1 {
return
}
vs.Validators = append(vs.Validators[:pos], vs.Validators[pos+1:]...)
}
// Implements sdk.ValidatorSet
func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, amt sdk.Rat) {
panic("not implemented")
}
// Implements sdk.ValidatorSet
func (vs *ValidatorSet) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
panic("not implemented")
}
// Implements sdk.ValidatorSet
func (vs *ValidatorSet) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
panic("not implemented")
}

View File

@ -32,7 +32,7 @@ func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
return nil, sdk.ErrTxDecode("accBytes are empty")
}
acct := new(AppAccount)
err = cdc.UnmarshalBinary(accBytes, &acct)
err = cdc.UnmarshalBinaryBare(accBytes, &acct)
if err != nil {
panic(err)
}

View File

@ -0,0 +1,107 @@
package assoc
import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// ValidatorSet defines
type ValidatorSet struct {
sdk.ValidatorSet
key sdk.KVStoreGetter
cdc *wire.Codec
maxAssoc int
addrLen int
}
var _ sdk.ValidatorSet = ValidatorSet{}
// NewValidatorSet returns new ValidatorSet with underlying ValidatorSet
func NewValidatorSet(cdc *wire.Codec, key sdk.KVStoreGetter, valset sdk.ValidatorSet, maxAssoc int, addrLen int) ValidatorSet {
if maxAssoc < 0 || addrLen < 0 {
panic("Cannot use negative integer for NewValidatorSet")
}
return ValidatorSet{
ValidatorSet: valset,
key: key,
cdc: cdc,
maxAssoc: maxAssoc,
addrLen: addrLen,
}
}
// Implements sdk.ValidatorSet
func (valset ValidatorSet) Validator(ctx sdk.Context, addr sdk.Address) (res sdk.Validator) {
store := valset.key.KVStore(ctx)
base := store.Get(GetBaseKey(addr))
res = valset.ValidatorSet.Validator(ctx, base)
if res == nil {
res = valset.ValidatorSet.Validator(ctx, addr)
}
return
}
// GetBaseKey :: sdk.Address -> sdk.Address
func GetBaseKey(addr sdk.Address) []byte {
return append([]byte{0x00}, addr...)
}
// GetAssocPrefix :: sdk.Address -> (sdk.Address -> byte)
func GetAssocPrefix(base sdk.Address) []byte {
return append([]byte{0x01}, base...)
}
// GetAssocKey :: (sdk.Address, sdk.Address) -> byte
func GetAssocKey(base sdk.Address, assoc sdk.Address) []byte {
return append(append([]byte{0x01}, base...), assoc...)
}
// Associate associates new address with validator address
func (valset ValidatorSet) Associate(ctx sdk.Context, base sdk.Address, assoc sdk.Address) bool {
if len(base) != valset.addrLen || len(assoc) != valset.addrLen {
return false
}
store := valset.key.KVStore(ctx)
// If someone already owns the associated address
if store.Get(GetBaseKey(assoc)) != nil {
return false
}
store.Set(GetBaseKey(assoc), base)
store.Set(GetAssocKey(base, assoc), []byte{0x00})
return true
}
// Dissociate removes association between addresses
func (valset ValidatorSet) Dissociate(ctx sdk.Context, base sdk.Address, assoc sdk.Address) bool {
if len(base) != valset.addrLen || len(assoc) != valset.addrLen {
return false
}
store := valset.key.KVStore(ctx)
// No associated address found for given validator
if !bytes.Equal(store.Get(GetBaseKey(assoc)), base) {
return false
}
store.Delete(GetBaseKey(assoc))
store.Delete(GetAssocKey(base, assoc))
return true
}
// Associations returns all associated addresses with a validator
func (valset ValidatorSet) Associations(ctx sdk.Context, base sdk.Address) (res []sdk.Address) {
store := valset.key.KVStore(ctx)
res = make([]sdk.Address, valset.maxAssoc)
iter := sdk.KVStorePrefixIterator(store, GetAssocPrefix(base))
i := 0
for ; iter.Valid(); iter.Next() {
key := iter.Key()
res[i] = key[len(key)-valset.addrLen:]
i++
}
return res[:i]
}

View File

@ -0,0 +1,71 @@
package assoc
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/examples/democoin/mock"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
func defaultContext(key sdk.StoreKey) sdk.Context {
db := dbm.NewMemDB()
cms := store.NewCommitMultiStore(db)
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
cms.LoadLatestVersion()
ctx := sdk.NewContext(cms, abci.Header{}, false, nil)
return ctx
}
func TestValidatorSet(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx := defaultContext(key)
addr1 := []byte("addr1")
addr2 := []byte("addr2")
base := &mock.ValidatorSet{[]mock.Validator{
{addr1, sdk.NewRat(1)},
{addr2, sdk.NewRat(2)},
}}
valset := NewValidatorSet(wire.NewCodec(), sdk.NewPrefixStoreGetter(key, []byte("assoc")), base, 1, 5)
assert.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
assert.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
assoc1 := []byte("asso1")
assoc2 := []byte("asso2")
assert.True(t, valset.Associate(ctx, addr1, assoc1))
assert.True(t, valset.Associate(ctx, addr2, assoc2))
assert.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, assoc1))
assert.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, assoc2))
assert.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
assert.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
assocs := valset.Associations(ctx, addr1)
assert.Equal(t, 1, len(assocs))
assert.True(t, bytes.Equal(assoc1, assocs[0]))
assert.False(t, valset.Associate(ctx, addr1, assoc2))
assert.False(t, valset.Associate(ctx, addr2, assoc1))
valset.Dissociate(ctx, addr1, assoc1)
valset.Dissociate(ctx, addr2, assoc2)
assert.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
assert.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
assert.Nil(t, valset.Validator(ctx, assoc1))
assert.Nil(t, valset.Validator(ctx, assoc2))
}

View File

@ -100,7 +100,7 @@ func TestMsgQuiz(t *testing.T) {
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, priv1) // the same answer will nolonger do!
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, priv1) // earlier answer now relavent again
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, priv1) // earlier answer now relevant again
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, priv1) // expect to fail to set the trend to something which is not cool
}

View File

@ -0,0 +1,58 @@
# Oracle Module
`x/oracle` provides a way to receive external information(real world price, events from other chains, etc.) with validators' vote. Each validator make transaction which contains those informations, and Oracle aggregates them until the supermajority signed on it. After then, Oracle sends the information to the actual module that processes the information, and prune the votes from the state.
## Integration
See `x/oracle/oracle_test.go` for the code that using Oracle
To use Oracle in your module, first define a `payload`. It should implement `oracle.Payload` and contain nessesary information for your module. Including nonce is recommended.
```go
type MyPayload struct {
Data int
Nonce int
}
```
When you write a payload, its `.Type()` should return same name with your module is registered on the router. It is because `oracle.Msg` inherits `.Type()` from its embedded payload and it should be handled on the user modules.
Then route every incoming `oracle.Msg` to `oracle.Keeper.Handler()` with the function that implements `oracle.Handler`.
```go
func NewHandler(keeper Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case oracle.Msg:
return keeper.oracle.Handle(ctx sdk.Context, p oracle.Payload) sdk.Error {
switch p := p.(type) {
case MyPayload:
return handleMyPayload(ctx, keeper, p)
}
}
}
}
}
```
In the previous example, the keeper has an `oracle.Keeper`. `oracle.Keeper`s are generated by `NewKeeper`.
```go
func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper {
return Keeper {
cdc: cdc,
key: key,
// ValidatorSet to get validators infor
valset: valset,
// The keeper will pass payload
// when more than 2/3 signed on it
supermaj: supermaj,
// The keeper will prune votes after 100 blocks from last sign
timeout: timeout,
}
}
```
Now the validators can send `oracle.Msg`s with `MyPayload` when they want to witness external events.

View File

@ -0,0 +1,31 @@
package oracle
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Oracle errors reserve 1101-1199
const (
CodeNotValidator sdk.CodeType = 1101
CodeAlreadyProcessed sdk.CodeType = 1102
CodeAlreadySigned sdk.CodeType = 1103
CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest
)
// ----------------------------------------
// Error constructors
// ErrNotValidator called when the signer of a Msg is not a validator
func ErrNotValidator(codespace sdk.CodespaceType, address sdk.Address) sdk.Error {
return sdk.NewError(codespace, CodeNotValidator, address.String())
}
// ErrAlreadyProcessed called when a payload is already processed
func ErrAlreadyProcessed(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeAlreadyProcessed, "")
}
// ErrAlreadySigned called when the signer is trying to double signing
func ErrAlreadySigned(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeAlreadySigned, "")
}

View File

@ -0,0 +1,105 @@
package oracle
import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Handler handles payload after it passes voting process
type Handler func(ctx sdk.Context, p Payload) sdk.Error
func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.ValidatorSet, p Payload, info Info) Info {
info.Power = info.Power.Add(val.GetPower())
// Return if the voted power is not bigger than required power
totalPower := valset.TotalPower(ctx)
requiredPower := totalPower.Mul(keeper.supermaj)
if !info.Power.GT(requiredPower) {
return info
}
// Check if the validators hash has been changed during the vote process
// and recalculate voted power
hash := ctx.BlockHeader().ValidatorsHash
if !bytes.Equal(hash, info.Hash) {
info.Power = sdk.ZeroRat()
info.Hash = hash
prefix := GetSignPrefix(p, keeper.cdc)
store := keeper.key.KVStore(ctx)
iter := sdk.KVStorePrefixIterator(store, prefix)
for ; iter.Valid(); iter.Next() {
if valset.Validator(ctx, iter.Value()) != nil {
store.Delete(iter.Key())
continue
}
info.Power = info.Power.Add(val.GetPower())
}
if !info.Power.GT(totalPower.Mul(keeper.supermaj)) {
return info
}
}
info.Status = Processed
return info
}
// Handle is used by other modules to handle Msg
func (keeper Keeper) Handle(h Handler, ctx sdk.Context, o Msg, codespace sdk.CodespaceType) sdk.Result {
valset := keeper.valset
signer := o.Signer
payload := o.Payload
// Check the oracle is not in process
info := keeper.Info(ctx, payload)
if info.Status != Pending {
return ErrAlreadyProcessed(codespace).Result()
}
// Check if it is reporting timeout
now := ctx.BlockHeight()
if now > info.LastSigned+keeper.timeout {
info = Info{Status: Timeout}
keeper.setInfo(ctx, payload, info)
keeper.clearSigns(ctx, payload)
return sdk.Result{}
}
info.LastSigned = ctx.BlockHeight()
// Check the signer is a validater
val := valset.Validator(ctx, signer)
if val == nil {
return ErrNotValidator(codespace, signer).Result()
}
// Check double signing
if keeper.signed(ctx, payload, signer) {
return ErrAlreadySigned(codespace).Result()
}
keeper.sign(ctx, payload, signer)
info = keeper.update(ctx, val, valset, payload, info)
if info.Status == Processed {
info = Info{Status: Processed}
}
keeper.setInfo(ctx, payload, info)
if info.Status == Processed {
keeper.clearSigns(ctx, payload)
cctx, write := ctx.CacheContext()
err := h(cctx, payload)
if err != nil {
return sdk.Result{
Code: sdk.ABCICodeOK,
Log: err.ABCILog(),
}
}
write()
}
return sdk.Result{}
}

View File

@ -0,0 +1,111 @@
package oracle
import (
"github.com/cosmos/cosmos-sdk/wire"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Keeper of the oracle store
type Keeper struct {
key sdk.KVStoreGetter
cdc *wire.Codec
valset sdk.ValidatorSet
supermaj sdk.Rat
timeout int64
}
// NewKeeper constructs a new keeper
func NewKeeper(key sdk.KVStoreGetter, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper {
if timeout < 0 {
panic("Timeout should not be negative")
}
return Keeper{
key: key,
cdc: cdc,
valset: valset,
supermaj: supermaj,
timeout: timeout,
}
}
// InfoStatus - current status of an Info
type InfoStatus int8
// Define InfoStatus
const (
Pending = InfoStatus(iota)
Processed
Timeout
)
// Info for each payload
type Info struct {
Power sdk.Rat
Hash []byte
LastSigned int64
Status InfoStatus
}
// EmptyInfo construct an empty Info
func EmptyInfo(ctx sdk.Context) Info {
return Info{
Power: sdk.ZeroRat(),
Hash: ctx.BlockHeader().ValidatorsHash,
LastSigned: ctx.BlockHeight(),
Status: Pending,
}
}
// Info returns the information about a payload
func (keeper Keeper) Info(ctx sdk.Context, p Payload) (res Info) {
store := keeper.key.KVStore(ctx)
key := GetInfoKey(p, keeper.cdc)
bz := store.Get(key)
if bz == nil {
return EmptyInfo(ctx)
}
keeper.cdc.MustUnmarshalBinary(bz, &res)
return
}
func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) {
store := keeper.key.KVStore(ctx)
key := GetInfoKey(p, keeper.cdc)
bz := keeper.cdc.MustMarshalBinary(info)
store.Set(key, bz)
}
func (keeper Keeper) sign(ctx sdk.Context, p Payload, signer sdk.Address) {
store := keeper.key.KVStore(ctx)
key := GetSignKey(p, signer, keeper.cdc)
store.Set(key, signer)
}
func (keeper Keeper) signed(ctx sdk.Context, p Payload, signer sdk.Address) bool {
store := keeper.key.KVStore(ctx)
key := GetSignKey(p, signer, keeper.cdc)
return store.Has(key)
}
func (keeper Keeper) clearSigns(ctx sdk.Context, p Payload) {
store := keeper.key.KVStore(ctx)
prefix := GetSignPrefix(p, keeper.cdc)
iter := sdk.KVStorePrefixIterator(store, prefix)
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
iter.Close()
}

View File

@ -0,0 +1,23 @@
package oracle
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// GetInfoKey returns the key for OracleInfo
func GetInfoKey(p Payload, cdc *wire.Codec) []byte {
bz := cdc.MustMarshalBinary(p)
return append([]byte{0x00}, bz...)
}
// GetSignPrefix returns the prefix for signs
func GetSignPrefix(p Payload, cdc *wire.Codec) []byte {
bz := cdc.MustMarshalBinary(p)
return append([]byte{0x01}, bz...)
}
// GetSignKey returns the key for sign
func GetSignKey(p Payload, signer sdk.Address, cdc *wire.Codec) []byte {
return append(GetSignPrefix(p, cdc), signer...)
}

View File

@ -0,0 +1,221 @@
package oracle
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/examples/democoin/mock"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
func defaultContext(keys ...sdk.StoreKey) sdk.Context {
db := dbm.NewMemDB()
cms := store.NewCommitMultiStore(db)
for _, key := range keys {
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
}
cms.LoadLatestVersion()
ctx := sdk.NewContext(cms, abci.Header{}, false, nil)
return ctx
}
type seqOracle struct {
Seq int
Nonce int
}
func (o seqOracle) Type() string {
return "seq"
}
func (o seqOracle) ValidateBasic() sdk.Error {
return nil
}
func makeCodec() *wire.Codec {
var cdc = wire.NewCodec()
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(Msg{}, "test/Oracle", nil)
cdc.RegisterInterface((*Payload)(nil), nil)
cdc.RegisterConcrete(seqOracle{}, "test/oracle/seqOracle", nil)
return cdc
}
func seqHandler(ork Keeper, key sdk.StoreKey, codespace sdk.CodespaceType) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case Msg:
return ork.Handle(func(ctx sdk.Context, p Payload) sdk.Error {
switch p := p.(type) {
case seqOracle:
return handleSeqOracle(ctx, key, p)
default:
return sdk.ErrUnknownRequest("")
}
}, ctx, msg, codespace)
default:
return sdk.ErrUnknownRequest("").Result()
}
}
}
func getSequence(ctx sdk.Context, key sdk.StoreKey) int {
store := ctx.KVStore(key)
seqbz := store.Get([]byte("seq"))
var seq int
if seqbz == nil {
seq = 0
} else {
wire.NewCodec().MustUnmarshalBinary(seqbz, &seq)
}
return seq
}
func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error {
store := ctx.KVStore(key)
seq := getSequence(ctx, key)
if seq != o.Seq {
return sdk.NewError(sdk.CodespaceUndefined, 1, "")
}
bz := wire.NewCodec().MustMarshalBinary(seq + 1)
store.Set([]byte("seq"), bz)
return nil
}
func TestOracle(t *testing.T) {
cdc := makeCodec()
addr1 := []byte("addr1")
addr2 := []byte("addr2")
addr3 := []byte("addr3")
addr4 := []byte("addr4")
valset := &mock.ValidatorSet{[]mock.Validator{
mock.Validator{addr1, sdk.NewRat(7)},
mock.Validator{addr2, sdk.NewRat(7)},
mock.Validator{addr3, sdk.NewRat(1)},
}}
key := sdk.NewKVStoreKey("testkey")
ctx := defaultContext(key)
bz, err := json.Marshal(valset)
require.Nil(t, err)
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
ork := NewKeeper(sdk.NewPrefixStoreGetter(key, []byte("oracle")), cdc, valset, sdk.NewRat(2, 3), 100)
h := seqHandler(ork, key, sdk.CodespaceUndefined)
// Nonmock.Validator signed, transaction failed
msg := Msg{seqOracle{0, 0}, []byte("randomguy")}
res := h(ctx, msg)
assert.False(t, res.IsOK())
assert.Equal(t, 0, getSequence(ctx, key))
// Less than 2/3 signed, msg not processed
msg.Signer = addr1
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 0, getSequence(ctx, key))
// Double signed, transaction failed
res = h(ctx, msg)
assert.False(t, res.IsOK())
assert.Equal(t, 0, getSequence(ctx, key))
// More than 2/3 signed, msg processed
msg.Signer = addr2
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// Already processed, transaction failed
msg.Signer = addr3
res = h(ctx, msg)
assert.False(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// Less than 2/3 signed, msg not processed
msg = Msg{seqOracle{100, 1}, addr1}
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// More than 2/3 signed but payload is invalid
msg.Signer = addr2
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.NotEqual(t, "", res.Log)
assert.Equal(t, 1, getSequence(ctx, key))
// Already processed, transaction failed
msg.Signer = addr3
res = h(ctx, msg)
assert.False(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// Should handle mock.Validator set change
valset.AddValidator(mock.Validator{addr4, sdk.NewRat(12)})
bz, err = json.Marshal(valset)
require.Nil(t, err)
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
// Less than 2/3 signed, msg not processed
msg = Msg{seqOracle{1, 2}, addr1}
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// Less than 2/3 signed, msg not processed
msg.Signer = addr2
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 1, getSequence(ctx, key))
// More than 2/3 signed, msg processed
msg.Signer = addr4
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 2, getSequence(ctx, key))
// Should handle mock.Validator set change while oracle process is happening
msg = Msg{seqOracle{2, 3}, addr4}
// Less than 2/3 signed, msg not processed
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 2, getSequence(ctx, key))
// Signed mock.Validator is kicked out
valset.RemoveValidator(addr4)
bz, err = json.Marshal(valset)
require.Nil(t, err)
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
// Less than 2/3 signed, msg not processed
msg.Signer = addr1
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 2, getSequence(ctx, key))
// More than 2/3 signed, msg processed
msg.Signer = addr2
res = h(ctx, msg)
assert.True(t, res.IsOK())
assert.Equal(t, 3, getSequence(ctx, key))
}

View File

@ -0,0 +1,33 @@
package oracle
import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Msg - struct for voting on payloads
type Msg struct {
Payload
Signer sdk.Address
}
// GetSignBytes implements sdk.Msg
func (msg Msg) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}
// GetSigners implements sdk.Msg
func (msg Msg) GetSigners() []sdk.Address {
return []sdk.Address{msg.Signer}
}
// Payload defines inner data for actual execution
type Payload interface {
Type() string
ValidateBasic() sdk.Error
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"net"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -46,7 +47,7 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error
if cmd.Name() == version.VersionCmd.Name() {
return nil
}
config, err := tcmd.ParseConfig()
config, err := interceptLoadConfig()
if err != nil {
return err
}
@ -65,6 +66,26 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error
}
}
// If a new config is created, change some of the default tendermint settings
func interceptLoadConfig() (conf *cfg.Config, err error) {
tmpConf := cfg.DefaultConfig()
viper.Unmarshal(tmpConf)
rootDir := tmpConf.RootDir
configFilePath := filepath.Join(rootDir, "config/config.toml")
// Intercept only if the file doesn't already exist
if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
// the following parse config is needed to create directories
sdkDefaultConfig, _ := tcmd.ParseConfig()
sdkDefaultConfig.ProfListenAddress = "prof_laddr=localhost:6060"
sdkDefaultConfig.P2P.RecvRate = 5120000
sdkDefaultConfig.P2P.SendRate = 5120000
cfg.WriteConfigFile(configFilePath, sdkDefaultConfig)
// Fall through, just so that its parsed into memory.
}
conf, err = tcmd.ParseConfig()
return
}
// add server commands
func AddCommands(
ctx *Context, cdc *wire.Codec,

View File

@ -170,9 +170,9 @@ func TestIAVLSubspaceIterator(t *testing.T) {
iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)})
expected2 := [][]byte{
[]byte{byte(55), byte(255), byte(255), byte(0)},
[]byte{byte(55), byte(255), byte(255), byte(1)},
[]byte{byte(55), byte(255), byte(255), byte(255)},
{byte(55), byte(255), byte(255), byte(0)},
{byte(55), byte(255), byte(255), byte(1)},
{byte(55), byte(255), byte(255), byte(255)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
@ -185,9 +185,9 @@ func TestIAVLSubspaceIterator(t *testing.T) {
iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)})
expected2 = [][]byte{
[]byte{byte(255), byte(255), byte(0)},
[]byte{byte(255), byte(255), byte(1)},
[]byte{byte(255), byte(255), byte(255)},
{byte(255), byte(255), byte(0)},
{byte(255), byte(255), byte(1)},
{byte(255), byte(255), byte(255)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
@ -229,9 +229,9 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)})
expected2 := [][]byte{
[]byte{byte(55), byte(255), byte(255), byte(255)},
[]byte{byte(55), byte(255), byte(255), byte(1)},
[]byte{byte(55), byte(255), byte(255), byte(0)},
{byte(55), byte(255), byte(255), byte(255)},
{byte(55), byte(255), byte(255), byte(1)},
{byte(55), byte(255), byte(255), byte(0)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]
@ -244,9 +244,9 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)})
expected2 = [][]byte{
[]byte{byte(255), byte(255), byte(255)},
[]byte{byte(255), byte(255), byte(1)},
[]byte{byte(255), byte(255), byte(0)},
{byte(255), byte(255), byte(255)},
{byte(255), byte(255), byte(1)},
{byte(255), byte(255), byte(0)},
}
for i = 0; iter.Valid(); iter.Next() {
expectedKey := expected2[i]

View File

@ -46,14 +46,24 @@ func (s prefixStore) Prefix(prefix []byte) KVStore {
// Implements KVStore
func (s prefixStore) Iterator(start, end []byte) Iterator {
if end == nil {
end = sdk.PrefixEndBytes(s.prefix)
} else {
end = append(s.prefix, end...)
}
return prefixIterator{
prefix: s.prefix,
iter: s.store.Iterator(start, end),
iter: s.store.Iterator(append(s.prefix, start...), end),
}
}
// Implements KVStore
func (s prefixStore) ReverseIterator(start, end []byte) Iterator {
if end == nil {
end = sdk.PrefixEndBytes(s.prefix)
} else {
end = append(s.prefix, end...)
}
return prefixIterator{
prefix: s.prefix,
iter: s.store.ReverseIterator(start, end),

View File

@ -15,13 +15,19 @@ import (
// Wait for the next tendermint block from the Tendermint RPC
// on localhost
func WaitForNextHeightTM(port string) {
WaitForNextNBlocksTM(1, port)
}
// Wait for N tendermint blocks to pass using the Tendermint RPC
// on localhost
func WaitForNextNBlocksTM(n int64, port string) {
url := fmt.Sprintf("http://localhost:%v", port)
cl := tmclient.NewHTTP(url, "/websocket")
resBlock, err := cl.Block(nil)
if err != nil {
panic(err)
}
waitForHeightTM(resBlock.Block.Height+1, url)
waitForHeightTM(resBlock.Block.Height+n, url)
}
// Wait for the given height from the Tendermint RPC

56
tools/Gopkg.lock generated
View File

@ -1,56 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/alecthomas/gometalinter"
packages = ["."]
revision = "46cc1ea3778b247666c2949669a3333c532fa9c6"
version = "v2.0.5"
[[projects]]
branch = "master"
name = "github.com/alecthomas/units"
packages = ["."]
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
name = "github.com/google/shlex"
packages = ["."]
revision = "6f45313302b9c56850fc17f99e40caebce98c716"
[[projects]]
name = "github.com/nicksnyder/go-i18n"
packages = [
"i18n",
"i18n/bundle",
"i18n/language",
"i18n/translation"
]
revision = "0dc1626d56435e9d605a29875701721c54bc9bbd"
version = "v1.10.0"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
[[projects]]
branch = "v3-unstable"
name = "gopkg.in/alecthomas/kingpin.v3-unstable"
packages = ["."]
revision = "b8d601de6db1f3b56a99ffe9051eb708574bc1cd"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
version = "v2.1.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "bb8cda576a5c4dda202435f43a46ae50a254181a4bf22c6af6f4d3d03079d509"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,34 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/alecthomas/gometalinter"
version = "2.0.5"
[prune]
go-tests = true
unused-packages = true

View File

@ -1,11 +1,15 @@
all: install
all: get_tools
########################################
### DEP
DEP = github.com/golang/dep/cmd/dep
GOLINT = github.com/tendermint/lint/golint
GOMETALINTER = gopkg.in/alecthomas/gometalinter.v2
DEP_CHECK := $(shell command -v dep 2> /dev/null)
GOLINT_CHECK := $(shell command -v golint 2> /dev/null)
GOMETALINTER_CHECK := $(shell command -v gometalinter.v2 2> /dev/null)
check_tools:
ifndef DEP_CHECK
@ -13,50 +17,46 @@ ifndef DEP_CHECK
else
@echo "Found dep in path."
endif
ifndef GOLINT_CHECK
@echo "No golint in path. Install with 'make get_tools'."
else
@echo "Found golint in path."
endif
ifndef GOMETALINTER_CHECK
@echo "No gometalinter in path. Install with 'make get_tools'."
else
@echo "Found gometalinter in path."
endif
get_tools:
ifdef DEP_CHECK
@echo "Dep is already installed. Run 'make update_tools' to update."
else
@echo "$(ansi_grn)Installing dep$(ansi_end)"
@echo "Installing dep"
go get -v $(DEP)
endif
ifdef GOLINT_CHECK
@echo "Golint is already installed. Run 'make update_tools' to update."
else
@echo "Installing golint"
go get -v $(GOLINT)
endif
ifdef GOMETALINTER_CHECK
@echo "Gometalinter.v2 is already installed. Run 'make update_tools' to update."
else
@echo "Installing gometalinter.v2"
go get -v $(GOMETALINTER)
endif
update_tools:
@echo "$(ansi_grn)Updating dep$(ansi_end)"
@echo "Updating dep"
go get -u -v $(DEP)
########################################
### Install tools
get_vendor_deps: check_tools
@rm -rf vendor/
@echo "--> Running dep ensure"
@dep ensure -v
install: get_vendor_deps
@echo "$(ansi_grn)Installing tools$(ansi_end)"
@echo "$(ansi_yel)Install go-vendorinstall$(ansi_end)"
go build -o bin/go-vendorinstall go-vendorinstall/*.go
@echo "$(ansi_yel)Install gometalinter.v2$(ansi_end)"
GOBIN="$(CURDIR)/bin" ./bin/go-vendorinstall github.com/alecthomas/gometalinter
@echo "$(ansi_grn)Done installing tools$(ansi_end)"
########################################
# ANSI colors
ansi_red=\033[0;31m
ansi_grn=\033[0;32m
ansi_yel=\033[0;33m
ansi_end=\033[0m
@echo "Updating tendermint/golint"
go get -u -v $(GOLINT)
@echo "Updating gometalinter.v2"
go get -u -v $(GOMETALINTER)
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check_tools install_tools update_tools get_vendor_deps install
.PHONY: check_tools get_tools update_tools

View File

View File

@ -1,129 +0,0 @@
// https://raw.githubusercontent.com/roboll/go-vendorinstall/a3e9f0a5d5861b3bb16b93200b2c359c9846b3c5/main.go
package main
import (
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
)
var (
source = flag.String("source", "vendor", "source directory")
target = flag.String("target", "", "target directory (defaults to $GOBIN, if not set $GOPATH/bin)")
commands = flag.String("commands", "", "comma separated list of commands to execute after go install in temporary environment")
quiet = flag.Bool("quiet", false, "disable output")
)
func main() {
flag.Parse()
packages := flag.Args()
if len(packages) < 1 {
fail(errors.New("no packages: specify a package"))
}
gopath, err := ioutil.TempDir("", "go-vendorinstall-gopath")
if err != nil {
fail(err)
}
print(fmt.Sprintf("gopath: %s", gopath))
defer func() {
if err := os.RemoveAll(gopath); err != nil {
fail(err)
}
}()
if len(*target) == 0 {
if gobin := os.Getenv("GOBIN"); len(gobin) > 0 {
target = &gobin
} else {
bin := fmt.Sprintf("%s/bin", os.Getenv("GOPATH"))
target = &bin
}
}
gobin, err := filepath.Abs(*target)
if err != nil {
fail(err)
}
print(fmt.Sprintf("gobin: %s", gobin))
if err := link(gopath, *source); err != nil {
fail(err)
}
oldpath := os.Getenv("PATH")
path := fmt.Sprintf("%s%s%s", gobin, string(os.PathListSeparator), os.Getenv("PATH"))
os.Setenv("PATH", fmt.Sprintf("%s%s%s", gobin, string(os.PathListSeparator), os.Getenv("PATH")))
defer os.Setenv("PATH", oldpath)
env := []string{fmt.Sprintf("PATH=%s", path), fmt.Sprintf("GOPATH=%s", gopath), fmt.Sprintf("GOBIN=%s", gobin)}
args := append([]string{"install"}, packages...)
if out, err := doexec("go", gopath, args, env); err != nil {
print(string(out))
fail(err)
}
if len(*commands) > 0 {
for _, cmd := range strings.Split(*commands, ",") {
split := strings.Split(cmd, " ")
if out, err := doexec(split[0], gopath, split[1:], env); err != nil {
print(string(out))
fail(err)
}
}
}
}
func print(msg string) {
if !*quiet {
fmt.Println(msg)
}
}
func fail(err error) {
fmt.Printf("error: %s", err.Error())
os.Exit(1)
}
func link(gopath, source string) error {
srcdir, err := filepath.Abs(source)
if err != nil {
return err
}
linkto := filepath.Join(gopath, "src")
if err := os.MkdirAll(linkto, 0777); err != nil {
return err
}
files, err := ioutil.ReadDir(srcdir)
if err != nil {
return err
}
for _, file := range files {
real := filepath.Join(srcdir, file.Name())
link := filepath.Join(linkto, file.Name())
if err := os.Symlink(real, link); err != nil {
return err
}
}
return nil
}
func doexec(bin, dir string, args []string, env []string) ([]byte, error) {
print(fmt.Sprintf("%s %s", bin, strings.Join(args, " ")))
cmd := exec.Command(bin, args...)
cmd.Env = env
cmd.Dir = dir
return cmd.CombinedOutput()
}

View File

@ -1,11 +0,0 @@
package main
// Include dependencies here so dep picks them up
// and installs sub-dependencies.
// TODO: Ideally this gets auto-imported on dep update.
// Any way to make that happen?
// NOTE: problems with this import because its a main not a lib
// _ "github.com/alecthomas/gometalinter"
func main() {}

View File

@ -80,7 +80,7 @@ func MustBech32ifyValPub(pub crypto.PubKey) string {
// create an Address from a string
func GetAccAddressHex(address string) (addr Address, err error) {
if len(address) == 0 {
return addr, errors.New("must use provide address")
return addr, errors.New("decoding bech32 address failed: must provide an address")
}
bz, err := hex.DecodeString(address)
if err != nil {
@ -116,7 +116,7 @@ func GetAccPubKeyBech32(address string) (pk crypto.PubKey, err error) {
// create an Address from a hex string
func GetValAddressHex(address string) (addr Address, err error) {
if len(address) == 0 {
return addr, errors.New("must use provide address")
return addr, errors.New("decoding bech32 address failed: must provide an address")
}
bz, err := hex.DecodeString(address)
if err != nil {
@ -152,7 +152,7 @@ func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
// decode a bytestring from a bech32-encoded string
func GetFromBech32(bech32str, prefix string) ([]byte, error) {
if len(bech32str) == 0 {
return nil, errors.New("must provide non-empty string")
return nil, errors.New("decoding bech32 address failed: must provide an address")
}
hrp, bz, err := bech32.DecodeAndConvert(bech32str)
if err != nil {

View File

@ -77,7 +77,7 @@ func CodeToDefaultMsg(code CodeType) string {
case CodeUnauthorized:
return "unauthorized"
case CodeInsufficientFunds:
return "insufficent funds"
return "insufficient funds"
case CodeUnknownRequest:
return "unknown request"
case CodeInvalidAddress:

View File

@ -1,7 +1,5 @@
package types
import ()
// Gas measured by the SDK
type Gas = int64

View File

@ -60,17 +60,17 @@ func unmarshalJSON(i *big.Int, bz []byte) error {
// Checks overflow, underflow and division by zero
// Exists in range from -(2^255-1) to 2^255-1
type Int struct {
i big.Int
i *big.Int
}
// BigInt converts Int to big.Int
func (i Int) BigInt() *big.Int {
return new(big.Int).Set(&(i.i))
return new(big.Int).Set(i.i)
}
// NewInt constructs Int from int64
func NewInt(n int64) Int {
return Int{*big.NewInt(n)}
return Int{big.NewInt(n)}
}
// NewIntFromBigInt constructs Int from big.Int
@ -78,7 +78,7 @@ func NewIntFromBigInt(i *big.Int) Int {
if i.BitLen() > 255 {
panic("NewIntFromBigInt() out of bound")
}
return Int{*i}
return Int{i}
}
// NewIntFromString constructs Int from string
@ -92,7 +92,7 @@ func NewIntFromString(s string) (res Int, ok bool) {
ok = false
return
}
return Int{*i}, true
return Int{i}, true
}
// NewIntWithDecimal constructs Int with decimal
@ -103,14 +103,14 @@ func NewIntWithDecimal(n int64, dec int) Int {
if i.BitLen() > 255 {
panic("NewIntWithDecimal() out of bound")
}
return Int{*i}
return Int{i}
}
// ZeroInt returns Int value with zero
func ZeroInt() Int { return Int{*big.NewInt(0)} }
func ZeroInt() Int { return Int{big.NewInt(0)} }
// OneInt returns Int value with one
func OneInt() Int { return Int{*big.NewInt(1)} }
func OneInt() Int { return Int{big.NewInt(1)} }
// Int64 converts Int to int64
// Panics if the value is out of range
@ -133,22 +133,22 @@ func (i Int) Sign() int {
// Equal compares two Ints
func (i Int) Equal(i2 Int) bool {
return equal(&(i.i), &(i2.i))
return equal(i.i, i2.i)
}
// GT returns true if first Int is greater than second
func (i Int) GT(i2 Int) bool {
return gt((&i.i), &(i2.i))
return gt(i.i, i2.i)
}
// LT returns true if first Int is lesser than second
func (i Int) LT(i2 Int) bool {
return lt((&i.i), &(i2.i))
return lt(i.i, i2.i)
}
// Add adds Int from another
func (i Int) Add(i2 Int) (res Int) {
res = Int{*add(&(i.i), &(i2.i))}
res = Int{add(i.i, i2.i)}
// Check overflow
if res.i.BitLen() > 255 {
panic("Int overflow")
@ -163,7 +163,7 @@ func (i Int) AddRaw(i2 int64) Int {
// Sub subtracts Int from another
func (i Int) Sub(i2 Int) (res Int) {
res = Int{*sub(&(i.i), &(i2.i))}
res = Int{sub(i.i, i2.i)}
// Check overflow
if res.i.BitLen() > 255 {
panic("Int overflow")
@ -182,7 +182,7 @@ func (i Int) Mul(i2 Int) (res Int) {
if i.i.BitLen()+i2.i.BitLen()-1 > 255 {
panic("Int overflow")
}
res = Int{*mul(&(i.i), &(i2.i))}
res = Int{mul(i.i, i2.i)}
// Check overflow if sign of both are same
if res.i.BitLen() > 255 {
panic("Int overflow")
@ -201,7 +201,7 @@ func (i Int) Div(i2 Int) (res Int) {
if i2.i.Sign() == 0 {
panic("Division by zero")
}
return Int{*div(&(i.i), &(i2.i))}
return Int{div(i.i, i2.i)}
}
// DivRaw divides Int with int64
@ -211,46 +211,58 @@ func (i Int) DivRaw(i2 int64) Int {
// Neg negates Int
func (i Int) Neg() (res Int) {
return Int{*neg(&(i.i))}
return Int{neg(i.i)}
}
// MarshalAmino defines custom encoding scheme
func (i Int) MarshalAmino() (string, error) {
return marshalAmino(&(i.i))
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return marshalAmino(i.i)
}
// UnmarshalAmino defines custom decoding scheme
func (i *Int) UnmarshalAmino(text string) error {
return unmarshalAmino(&(i.i), text)
if i.i == nil { // Necessary since default Int initialization has i.i as nil
i.i = new(big.Int)
}
return unmarshalAmino(i.i, text)
}
// MarshalJSON defines custom encoding scheme
func (i Int) MarshalJSON() ([]byte, error) {
return marshalJSON(&(i.i))
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return marshalJSON(i.i)
}
// UnmarshalJSON defines custom decoding scheme
func (i *Int) UnmarshalJSON(bz []byte) error {
return unmarshalJSON(&(i.i), bz)
if i.i == nil { // Necessary since default Int initialization has i.i as nil
i.i = new(big.Int)
}
return unmarshalJSON(i.i, bz)
}
// Int wraps integer with 256 bit range bound
// Checks overflow, underflow and division by zero
// Exists in range from 0 to 2^256-1
type Uint struct {
i big.Int
i *big.Int
}
// BigInt converts Uint to big.Unt
func (i Uint) BigInt() *big.Int {
return new(big.Int).Set(&(i.i))
return new(big.Int).Set(i.i)
}
// NewUint constructs Uint from int64
func NewUint(n uint64) Uint {
i := new(big.Int)
i.SetUint64(n)
return Uint{*i}
return Uint{i}
}
// NewUintFromBigUint constructs Uint from big.Uint
@ -259,7 +271,7 @@ func NewUintFromBigInt(i *big.Int) Uint {
if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 {
panic("Uint overflow")
}
return Uint{*i}
return Uint{i}
}
// NewUintFromString constructs Uint from string
@ -273,7 +285,7 @@ func NewUintFromString(s string) (res Uint, ok bool) {
ok = false
return
}
return Uint{*i}, true
return Uint{i}, true
}
// NewUintWithDecimal constructs Uint with decimal
@ -284,14 +296,14 @@ func NewUintWithDecimal(n int64, dec int) Uint {
if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 {
panic("NewUintWithDecimal() out of bound")
}
return Uint{*i}
return Uint{i}
}
// ZeroUint returns Uint value with zero
func ZeroUint() Uint { return Uint{*big.NewInt(0)} }
func ZeroUint() Uint { return Uint{big.NewInt(0)} }
// OneUint returns Uint value with one
func OneUint() Uint { return Uint{*big.NewInt(1)} }
func OneUint() Uint { return Uint{big.NewInt(1)} }
// Uint64 converts Uint to uint64
// Panics if the value is out of range
@ -314,22 +326,22 @@ func (i Uint) Sign() int {
// Equal compares two Uints
func (i Uint) Equal(i2 Uint) bool {
return equal(&(i.i), &(i2.i))
return equal(i.i, i2.i)
}
// GT returns true if first Uint is greater than second
func (i Uint) GT(i2 Uint) bool {
return gt(&(i.i), &(i2.i))
return gt(i.i, i2.i)
}
// LT returns true if first Uint is lesser than second
func (i Uint) LT(i2 Uint) bool {
return lt(&(i.i), &(i2.i))
return lt(i.i, i2.i)
}
// Add adds Uint from another
func (i Uint) Add(i2 Uint) (res Uint) {
res = Uint{*add(&(i.i), &(i2.i))}
res = Uint{add(i.i, i2.i)}
// Check overflow
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow")
@ -344,7 +356,7 @@ func (i Uint) AddRaw(i2 uint64) Uint {
// Sub subtracts Uint from another
func (i Uint) Sub(i2 Uint) (res Uint) {
res = Uint{*sub(&(i.i), &(i2.i))}
res = Uint{sub(i.i, i2.i)}
// Check overflow
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow")
@ -363,7 +375,7 @@ func (i Uint) Mul(i2 Uint) (res Uint) {
if i.i.BitLen()+i2.i.BitLen()-1 > 256 {
panic("Uint overflow")
}
res = Uint{*mul(&(i.i), &(i2.i))}
res = Uint{mul(i.i, i2.i)}
// Check overflow
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow")
@ -382,7 +394,7 @@ func (i Uint) Div(i2 Uint) (res Uint) {
if i2.Sign() == 0 {
panic("division-by-zero")
}
return Uint{*div(&(i.i), &(i2.i))}
return Uint{div(i.i, i2.i)}
}
// Div divides Uint with int64
@ -392,20 +404,32 @@ func (i Uint) DivRaw(i2 uint64) Uint {
// MarshalAmino defines custom encoding scheme
func (i Uint) MarshalAmino() (string, error) {
return marshalAmino(&(i.i))
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return marshalAmino(i.i)
}
// UnmarshalAmino defines custom decoding scheme
func (i *Uint) UnmarshalAmino(text string) error {
return unmarshalAmino(&(i.i), text)
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return unmarshalAmino(i.i, text)
}
// MarshalJSON defines custom encoding scheme
func (i Uint) MarshalJSON() ([]byte, error) {
return marshalJSON(&(i.i))
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return marshalJSON(i.i)
}
// UnmarshalJSON defines custom decoding scheme
func (i *Uint) UnmarshalJSON(bz []byte) error {
return unmarshalJSON(&(i.i), bz)
if i.i == nil { // Necessary since default Uint initialization has i.i as nil
i.i = new(big.Int)
}
return unmarshalJSON(i.i, bz)
}

View File

@ -18,20 +18,20 @@ import (
// we will panic unmarshalling into the
// nil embedded big.Rat
type Rat struct {
big.Rat `json:"rat"`
*big.Rat `json:"rat"`
}
// nolint - common values
func ZeroRat() Rat { return Rat{*big.NewRat(0, 1)} }
func OneRat() Rat { return Rat{*big.NewRat(1, 1)} }
func ZeroRat() Rat { return Rat{big.NewRat(0, 1)} }
func OneRat() Rat { return Rat{big.NewRat(1, 1)} }
// New - create a new Rat from integers
func NewRat(Numerator int64, Denominator ...int64) Rat {
switch len(Denominator) {
case 0:
return Rat{*big.NewRat(Numerator, 1)}
return Rat{big.NewRat(Numerator, 1)}
case 1:
return Rat{*big.NewRat(Numerator, Denominator[0])}
return Rat{big.NewRat(Numerator, Denominator[0])}
default:
panic("improper use of New, can only have one denominator")
}
@ -84,9 +84,9 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
func NewRatFromBigInt(num *big.Int, denom ...*big.Int) Rat {
switch len(denom) {
case 0:
return Rat{*new(big.Rat).SetInt(num)}
return Rat{new(big.Rat).SetInt(num)}
case 1:
return Rat{*new(big.Rat).SetFrac(num, denom[0])}
return Rat{new(big.Rat).SetFrac(num, denom[0])}
default:
panic("improper use of NewRatFromBigInt, can only have one denominator")
}
@ -96,26 +96,29 @@ func NewRatFromBigInt(num *big.Int, denom ...*big.Int) Rat {
func NewRatFromInt(num Int, denom ...Int) Rat {
switch len(denom) {
case 0:
return Rat{*new(big.Rat).SetInt(num.BigInt())}
return Rat{new(big.Rat).SetInt(num.BigInt())}
case 1:
return Rat{*new(big.Rat).SetFrac(num.BigInt(), denom[0].BigInt())}
return Rat{new(big.Rat).SetFrac(num.BigInt(), denom[0].BigInt())}
default:
panic("improper use of NewRatFromBigInt, can only have one denominator")
}
}
//nolint
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 }
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // greater than
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // less than
func (r Rat) Mul(r2 Rat) Rat { return Rat{*new(big.Rat).Mul(&(r.Rat), &(r2.Rat))} } // Mul - multiplication
func (r Rat) Quo(r2 Rat) Rat { return Rat{*new(big.Rat).Quo(&(r.Rat), &(r2.Rat))} } // Quo - quotient
func (r Rat) Add(r2 Rat) Rat { return Rat{*new(big.Rat).Add(&(r.Rat), &(r2.Rat))} } // Add - addition
func (r Rat) Sub(r2 Rat) Rat { return Rat{*new(big.Rat).Sub(&(r.Rat), &(r2.Rat))} } // Sub - subtraction
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) }
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 }
func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than
func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal
func (r Rat) LT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == -1 } // less than
func (r Rat) LTE(r2 Rat) bool { return !r.GT(r2) } // less than or equal
func (r Rat) Mul(r2 Rat) Rat { return Rat{new(big.Rat).Mul(r.Rat, r2.Rat)} } // Mul - multiplication
func (r Rat) Quo(r2 Rat) Rat { return Rat{new(big.Rat).Quo(r.Rat, r2.Rat)} } // Quo - quotient
func (r Rat) Add(r2 Rat) Rat { return Rat{new(big.Rat).Add(r.Rat, r2.Rat)} } // Add - addition
func (r Rat) Sub(r2 Rat) Rat { return Rat{new(big.Rat).Sub(r.Rat, r2.Rat)} } // Sub - subtraction
func (r Rat) String() string { return r.Rat.String() }
func (r Rat) FloatString() string { return r.Rat.FloatString(10) } // a human-friendly string format. The last digit is rounded to nearest, with halves rounded away from zero.
var (
zero = big.NewInt(0)
@ -168,8 +171,8 @@ func (r Rat) EvaluateInt() Int {
// round Rat with the provided precisionFactor
func (r Rat) Round(precisionFactor int64) Rat {
rTen := Rat{*new(big.Rat).Mul(&(r.Rat), big.NewRat(precisionFactor, 1))}
return Rat{*big.NewRat(rTen.Evaluate(), precisionFactor)}
rTen := Rat{new(big.Rat).Mul(r.Rat, big.NewRat(precisionFactor, 1))}
return Rat{big.NewRat(rTen.Evaluate(), precisionFactor)}
}
// TODO panic if negative or if totalDigits < len(initStr)???
@ -184,7 +187,10 @@ func (r Rat) ToLeftPadded(totalDigits int8) string {
//Wraps r.MarshalText().
func (r Rat) MarshalAmino() (string, error) {
bz, err := (&(r.Rat)).MarshalText()
if r.Rat == nil {
r.Rat = new(big.Rat)
}
bz, err := r.Rat.MarshalText()
return string(bz), err
}
@ -195,7 +201,7 @@ func (r *Rat) UnmarshalAmino(text string) (err error) {
if err != nil {
return err
}
r.Rat = *tempRat
r.Rat = tempRat
return nil
}

View File

@ -100,7 +100,7 @@ func TestEqualities(t *testing.T) {
}
func TestArithmatic(t *testing.T) {
func TestArithmetic(t *testing.T) {
tests := []struct {
r1, r2 Rat
resMul, resDiv, resAdd, resSub Rat
@ -180,8 +180,8 @@ func TestRound(t *testing.T) {
precFactor int64
}{
{NewRat(333, 777), NewRat(429, 1000), 1000},
{Rat{*new(big.Rat).SetFrac(big3, big7)}, NewRat(429, 1000), 1000},
{Rat{*new(big.Rat).SetFrac(big3, big7)}, Rat{*big.NewRat(4285714286, 10000000000)}, 10000000000},
{Rat{new(big.Rat).SetFrac(big3, big7)}, NewRat(429, 1000), 1000},
{Rat{new(big.Rat).SetFrac(big3, big7)}, Rat{big.NewRat(4285714286, 10000000000)}, 10000000000},
{NewRat(1, 2), NewRat(1, 2), 1000},
}
@ -229,7 +229,7 @@ func TestSerializationText(t *testing.T) {
bz, err := r.MarshalText()
require.NoError(t, err)
var r2 Rat
var r2 = Rat{new(big.Rat)}
err = r2.UnmarshalText(bz)
require.NoError(t, err)
assert.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2)
@ -297,3 +297,14 @@ func TestRatsEqual(t *testing.T) {
}
}
func TestStringOverflow(t *testing.T) {
// two random 64 bit primes
rat1 := NewRat(5164315003622678713, 4389711697696177267)
rat2 := NewRat(-3179849666053572961, 8459429845579852627)
rat3 := rat1.Add(rat2)
assert.Equal(t,
"29728537197630860939575850336935951464/37134458148982045574552091851127630409",
rat3.String(),
)
}

View File

@ -59,8 +59,9 @@ type ValidatorSet interface {
IterateValidatorsBonded(Context,
func(index int64, validator Validator) (stop bool))
Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set
Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set
Slash(Context, crypto.PubKey, int64, Rat) // slash the validator and delegators of the validator, specifying offence height & slash fraction
Revoke(Context, crypto.PubKey) // revoke a validator
Unrevoke(Context, crypto.PubKey) // unrevoke a validator

View File

@ -21,8 +21,8 @@ func (t Tags) AppendTag(k string, v []byte) Tags {
}
// Append two lists of tags
func (t Tags) AppendTags(a Tags) Tags {
return append(t, a...)
func (t Tags) AppendTags(tags Tags) Tags {
return append(t, tags...)
}
// Turn tags into KVPair list
@ -51,3 +51,13 @@ func NewTags(tags ...interface{}) Tags {
func MakeTag(k string, v []byte) Tag {
return Tag{Key: []byte(k), Value: v}
}
//__________________________________________________
// common tags
var (
TagAction = "action"
TagSrcValidator = "source-validator"
TagDstValidator = "destination-validator"
TagDelegator = "delegator"
)

View File

@ -31,9 +31,6 @@ type Tx interface {
// Gets the Msg.
GetMsgs() []Msg
// Gets the memo.
GetMemo() string
}
//__________________________________________________________

View File

@ -35,3 +35,15 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
}
return out.Bytes(), nil
}
//__________________________________________________________________
// generic sealed codec to be used throughout sdk
var Cdc *Codec
func init() {
cdc := NewCodec()
RegisterCrypto(cdc)
Cdc = cdc
//Cdc = cdc.Seal() // TODO uncomment once amino upgraded to 0.9.10
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/viper"
)
const (
@ -38,7 +37,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
true
}
memo := tx.GetMemo()
memo := stdTx.GetMemo()
if len(memo) > maxMemoCharacters {
return ctx,
@ -72,12 +71,6 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
accNums[i] = sigs[i].AccountNumber
}
fee := stdTx.Fee
chainID := ctx.ChainID()
// XXX: major hack; need to get ChainID
// into the app right away (#565)
if chainID == "" {
chainID = viper.GetString("chain-id")
}
// Check sig and nonce and collect signer accounts.
var signerAccs = make([]Account, len(signerAddrs))
@ -85,7 +78,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
signerAddr, sig := signerAddrs[i], sigs[i]
// check signature, return account with incremented nonce
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], sequences[i], fee, msgs, tx.GetMemo())
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
signerAcc, res := processSig(
ctx, am,
signerAddr, sig, signBytes,

View File

@ -1,34 +0,0 @@
package auth
import (
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// NewHandler returns a handler for "baseaccount" type messages.
func NewHandler(am AccountMapper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case MsgChangeKey:
return handleMsgChangeKey(ctx, am, msg)
default:
errMsg := "Unrecognized baseaccount Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// Handle MsgChangeKey
// Should be very expensive, because once this happens, an account is un-prunable
func handleMsgChangeKey(ctx sdk.Context, am AccountMapper, msg MsgChangeKey) sdk.Result {
err := am.SetPubKey(ctx, msg.Address, msg.NewPubKey)
if err != nil {
return err.Result()
}
return sdk.Result{
Tags: sdk.NewTags("action", []byte("changePubkey"), "address", msg.Address.Bytes(), "pubkey", msg.NewPubKey.Bytes()),
}
}

View File

@ -100,17 +100,6 @@ func (am AccountMapper) GetPubKey(ctx sdk.Context, addr sdk.Address) (crypto.Pub
return acc.GetPubKey(), nil
}
// Sets the PubKey of the account at address
func (am AccountMapper) SetPubKey(ctx sdk.Context, addr sdk.Address, newPubKey crypto.PubKey) sdk.Error {
acc := am.GetAccount(ctx, addr)
if acc == nil {
return sdk.ErrUnknownAddress(addr.String())
}
acc.SetPubKey(newPubKey)
am.SetAccount(ctx, acc)
return nil
}
// Returns the Sequence of the account at address
func (am AccountMapper) GetSequence(ctx sdk.Context, addr sdk.Address) (int64, sdk.Error) {
acc := am.GetAccount(ctx, addr)

View File

@ -8,12 +8,33 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-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 (
@ -23,26 +44,21 @@ var (
addr2 = priv2.PubKey().Address()
coins = sdk.Coins{sdk.NewCoin("foocoin", 10)}
sendMsg1 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
testMsg1 = testMsg{signers: []sdk.Address{addr1}, positiveNum: 1}
)
// initialize the mock application for this module
func getMockApp(t *testing.T) *App {
mapp := NewApp()
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("bank", bank.NewHandler(coinKeeper))
mapp.Router().AddRoute("auth", auth.NewHandler(mapp.AccountMapper))
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 TestMsgChangePubKey(t *testing.T) {
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
@ -62,37 +78,16 @@ func TestMsgChangePubKey(t *testing.T) {
assert.Equal(t, acc1, res1.(*auth.BaseAccount))
// Run a CheckDeliver
SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1}, []int64{0}, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("foocoin", 67)})
CheckBalance(t, mapp, addr2, sdk.Coins{sdk.NewCoin("foocoin", 10)})
changePubKeyMsg := auth.MsgChangeKey{
Address: addr1,
NewPubKey: priv2.PubKey(),
}
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{})
ctxDeliver := mapp.BaseApp.NewContext(false, abci.Header{})
acc2 := mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
// send a MsgChangePubKey
SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{changePubKeyMsg}, []int64{0}, []int64{1}, true, priv1)
acc2 = mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
assert.True(t, priv2.PubKey().Equals(acc2.GetPubKey()))
// signing a SendMsg with the old privKey should be an auth error
mapp.BeginBlock(abci.RequestBeginBlock{})
tx := GenTx([]sdk.Msg{sendMsg1}, []int64{0}, []int64{2}, priv1)
tx := GenTx([]sdk.Msg{testMsg1}, []int64{0}, []int64{1}, priv2)
res := mapp.Deliver(tx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the new correct priv key should work
SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1}, []int64{0}, []int64{2}, true, priv2)
// 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)
// Check balances
CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("foocoin", 57)})
CheckBalance(t, mapp, addr2, sdk.Coins{sdk.NewCoin("foocoin", 20)})
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), res.Code, res.Log)
}

View File

@ -54,6 +54,23 @@ func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyE
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(t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.Result {
tx := GenTx(msgs, accnums, seq, priv...)
@ -62,7 +79,7 @@ func SignCheck(t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accnums []int
}
// simulate a block
func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accnums []int64, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
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...)
@ -86,4 +103,5 @@ func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accnum
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
return res
}

View File

@ -1,41 +0,0 @@
package auth
import (
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
// MsgChangeKey - high level transaction of the auth module
type MsgChangeKey struct {
Address sdk.Address `json:"address"`
NewPubKey crypto.PubKey `json:"public_key"`
}
var _ sdk.Msg = MsgChangeKey{}
// NewMsgChangeKey - msg to claim an account and set the PubKey
func NewMsgChangeKey(addr sdk.Address, pubkey crypto.PubKey) MsgChangeKey {
return MsgChangeKey{Address: addr, NewPubKey: pubkey}
}
// Implements Msg.
func (msg MsgChangeKey) Type() string { return "auth" }
// Implements Msg.
func (msg MsgChangeKey) ValidateBasic() sdk.Error {
return nil
}
// Implements Msg.
func (msg MsgChangeKey) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
if err != nil {
panic(err)
}
return b
}
// Implements Msg.
func (msg MsgChangeKey) GetSigners() []sdk.Address {
return []sdk.Address{msg.Address}
}

View File

@ -1,47 +0,0 @@
package auth
import (
"testing"
"github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestNewMsgChangeKey(t *testing.T) {}
func TestMsgChangeKeyType(t *testing.T) {
addr1 := sdk.Address([]byte("input"))
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
var msg = MsgChangeKey{
Address: addr1,
NewPubKey: newPubKey,
}
assert.Equal(t, msg.Type(), "auth")
}
func TestMsgChangeKeyValidation(t *testing.T) {
addr1 := sdk.Address([]byte("input"))
// emptyPubKey := crypto.PubKeyEd25519{}
// var msg = MsgChangeKey{
// Address: addr1,
// NewPubKey: emptyPubKey,
// }
// // fmt.Println(msg.NewPubKey.Empty())
// fmt.Println(msg.NewPubKey.Bytes())
// assert.NotNil(t, msg.ValidateBasic())
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
msg := MsgChangeKey{
Address: addr1,
NewPubKey: newPubKey,
}
assert.Nil(t, msg.ValidateBasic())
}

View File

@ -34,7 +34,7 @@ func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs }
// Addresses are returned in a determistic order.
// They are accumulated from the GetSigners method for each Msg
// in the order they appear in tx.GetMsgs().
// Duplicate addresses will be ommitted.
// Duplicate addresses will be omitted.
func (tx StdTx) GetSigners() []sdk.Address {
seen := map[string]bool{}
var signers []sdk.Address

View File

@ -8,7 +8,6 @@ import (
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterInterface((*Account)(nil), nil)
cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil)
cdc.RegisterConcrete(MsgChangeKey{}, "auth/ChangeKey", nil)
cdc.RegisterConcrete(StdTx{}, "auth/StdTx", nil)
}

View File

@ -77,14 +77,22 @@ var (
// initialize the mock application for this module
func getMockApp(t *testing.T) *mock.App {
mapp, err := getBenchmarkMockApp()
require.NoError(t, err)
return mapp
}
// getBenchmarkMockApp initializes a mock application for this module, for purposes of benchmarking
// Any long term API support commitments do not apply to this function.
func getBenchmarkMockApp() (*mock.App, error) {
mapp := mock.NewApp()
RegisterWire(mapp.Cdc)
coinKeeper := NewKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("bank", NewHandler(coinKeeper))
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{}))
return mapp
err := mapp.CompleteSetup([]*sdk.KVStoreKey{})
return mapp, err
}
func TestMsgSendWithAccounts(t *testing.T) {

41
x/bank/bench_test.go Normal file
View File

@ -0,0 +1,41 @@
package bank
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/mock"
abci "github.com/tendermint/abci/types"
)
func BenchmarkOneBankSendTxPerBlock(b *testing.B) {
benchmarkApp, _ := getBenchmarkMockApp()
// Add an account at genesis
acc := &auth.BaseAccount{
Address: addr1,
// Some value conceivably higher than the benchmarks would ever go
Coins: sdk.Coins{sdk.NewCoin("foocoin", 100000000000)},
}
accs := []auth.Account{acc}
// Construct genesis state
mock.SetGenesis(benchmarkApp, accs)
// Precompute all txs
txs := mock.GenSequenceOfTxs([]sdk.Msg{sendMsg1}, []int64{0}, []int64{int64(0)}, b.N, priv1)
b.ResetTimer()
// Run this with a profiler, so its easy to distinguish what time comes from
// Committing, and what time comes from Check/Deliver Tx.
for i := 0; i < b.N; i++ {
benchmarkApp.BeginBlock(abci.RequestBeginBlock{})
x := benchmarkApp.Check(txs[i])
if !x.IsOK() {
panic("something is broken in checking transaction")
}
benchmarkApp.Deliver(txs[i])
benchmarkApp.EndBlock(abci.RequestEndBlock{})
benchmarkApp.Commit()
}
}

View File

@ -132,13 +132,13 @@ func TestMsgSendValidation(t *testing.T) {
}{
{false, MsgSend{}}, // no input or output
{false, MsgSend{Inputs: []Input{input1}}}, // just input
{false, MsgSend{Outputs: []Output{output1}}}, // just ouput
{false, MsgSend{Outputs: []Output{output1}}}, // just output
{false, MsgSend{
Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input
Outputs: []Output{output1}}},
{false, MsgSend{
Inputs: []Input{input1},
Outputs: []Output{{emptyAddr, atom123}}}, // invalid ouput
Outputs: []Output{{emptyAddr, atom123}}}, // invalid output
},
{false, MsgSend{
Inputs: []Input{input1},

View File

@ -243,7 +243,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr
return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false
}
// Subtract coins from depositers account
// Subtract coins from depositer's account
_, _, err := keeper.ck.SubtractCoins(ctx, depositerAddr, depositAmount)
if err != nil {
return err, false

View File

@ -57,7 +57,11 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
stakeGenesis := stake.DefaultGenesisState()
stakeGenesis.Pool.LooseTokens = 100000
stake.InitGenesis(ctx, stakeKeeper, stakeGenesis)
InitGenesis(ctx, keeper, DefaultGenesisState())
return abci.ResponseInitChain{}
}

View File

@ -55,7 +55,9 @@ func getEndBlocker(keeper stake.Keeper) sdk.EndBlocker {
func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
stake.InitGenesis(ctx, keeper, stake.DefaultGenesisState())
stakeGenesis := stake.DefaultGenesisState()
stakeGenesis.Pool.LooseTokens = 100000
stake.InitGenesis(ctx, keeper, stakeGenesis)
return abci.ResponseInitChain{}
}
}

View File

@ -12,41 +12,16 @@ const (
// Default slashing codespace
DefaultCodespace sdk.CodespaceType = 10
// Invalid validator
CodeInvalidValidator CodeType = 201
// Validator jailed
CodeValidatorJailed CodeType = 202
CodeInvalidValidator CodeType = 101
CodeValidatorJailed CodeType = 102
)
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
}
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
}
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
}
func codeToDefaultMsg(code CodeType) string {
switch code {
case CodeInvalidValidator:
return "invalid Validator"
case CodeValidatorJailed:
return "validator Jailed"
default:
return sdk.CodeToDefaultMsg(code)
}
}
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
}
return codeToDefaultMsg(code)
}
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(codespace, code, msg)
return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
}

View File

@ -80,7 +80,7 @@ func TestHandleAbsentValidator(t *testing.T) {
validator, _ := sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.Equal(t, int64(100), pool.BondedTokens.Int64())
require.Equal(t, int64(100), pool.BondedTokens)
// 51st block missed
ctx = ctx.WithBlockHeight(height)
@ -109,7 +109,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// validator should have been slashed
pool = sk.GetPool(ctx)
require.Equal(t, int64(99), pool.BondedTokens.Int64())
require.Equal(t, int64(99), pool.BondedTokens)
// validator start height should have been changed
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
@ -167,5 +167,5 @@ func TestHandleNewValidator(t *testing.T) {
validator, _ := sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.Equal(t, int64(100), pool.BondedTokens.Int64())
require.Equal(t, int64(100), pool.BondedTokens)
}

View File

@ -20,6 +20,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/stake"
)
// TODO remove dependencies on staking (should only refer to validator set type from sdk)
var (
addrs = []sdk.Address{
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6160"),
@ -61,7 +63,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep
ck := bank.NewKeeper(accountMapper)
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
genesis := stake.DefaultGenesisState()
genesis.Pool.LooseUnbondedTokens = initCoins.MulRaw(int64(len(addrs)))
genesis.Pool.LooseTokens = initCoins.MulRaw(int64(len(addrs))).Int64()
stake.InitGenesis(ctx, sk, genesis)
for _, addr := range addrs {
ck.AddCoins(ctx, addr, sdk.Coins{
@ -89,9 +91,9 @@ func testAddr(addr string) sdk.Address {
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
return stake.MsgCreateValidator{
Description: stake.Description{},
ValidatorAddr: address,
PubKey: pubKey,
Bond: sdk.Coin{"steak", amt},
Description: stake.Description{},
ValidatorAddr: address,
PubKey: pubKey,
SelfDelegation: sdk.Coin{"steak", amt},
}
}

View File

@ -22,9 +22,9 @@ var (
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
priv4 = crypto.GenPrivKeyEd25519()
addr4 = priv4.PubKey().Address()
coins = sdk.Coins{sdk.NewCoin("foocoin", 10)}
coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}}
fee = auth.StdFee{
sdk.Coins{sdk.NewCoin("foocoin", 0)},
sdk.Coins{{"foocoin", sdk.NewInt(0)}},
100000,
}
)
@ -60,7 +60,9 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
mapp.InitChainer(ctx, req)
InitGenesis(ctx, keeper, DefaultGenesisState())
stakeGenesis := DefaultGenesisState()
stakeGenesis.Pool.LooseTokens = 100000
InitGenesis(ctx, keeper, stakeGenesis)
return abci.ResponseInitChain{}
}
@ -93,8 +95,8 @@ func checkDelegation(t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
func TestStakeMsgs(t *testing.T) {
mapp, keeper := getMockApp(t)
genCoin := sdk.NewCoin("steak", 42)
bondCoin := sdk.NewCoin("steak", 10)
genCoin := sdk.Coin{"steak", sdk.NewInt(42)}
bondCoin := sdk.Coin{"steak", sdk.NewInt(10)}
acc1 := &auth.BaseAccount{
Address: addr1,
@ -148,10 +150,14 @@ func TestStakeMsgs(t *testing.T) {
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
////////////////////
// Unbond
// Begin Unbonding
unbondMsg := NewMsgUnbond(addr2, addr1, "MAX")
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unbondMsg}, []int64{1}, []int64{1}, true, priv2)
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10))
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2)
// delegation should exist anymore
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
// balance should be the same because bonding not yet complete
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
}

View File

@ -6,11 +6,14 @@ import (
// nolint
const (
FlagAddressDelegator = "address-delegator"
FlagAddressValidator = "address-validator"
FlagPubKey = "pubkey"
FlagAmount = "amount"
FlagShares = "shares"
FlagAddressDelegator = "address-delegator"
FlagAddressValidator = "address-validator"
FlagAddressValidatorSrc = "addr-validator-source"
FlagAddressValidatorDst = "addr-validator-dest"
FlagPubKey = "pubkey"
FlagAmount = "amount"
FlagSharesAmount = "shares-amount"
FlagSharesPercent = "shares-percent"
FlagMoniker = "moniker"
FlagIdentity = "keybase-sig"
@ -20,22 +23,26 @@ const (
// common flagsets to add to various functions
var (
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError)
)
func init() {
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220")
fsAmount.String(FlagAmount, "1steak", "Amount of coins to bond")
fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)")
fsDescription.String(FlagMoniker, "", "validator name")
fsDescription.String(FlagIdentity, "", "optional keybase signature")
fsDescription.String(FlagWebsite, "", "optional website")
fsDescription.String(FlagDetails, "", "optional details")
fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal")
fsShares.String(FlagSharesPercent, "", "Percent of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1")
fsDescription.String(FlagMoniker, "[do-not-modify]", "validator name")
fsDescription.String(FlagIdentity, "[do-not-modify]", "optional keybase signature")
fsDescription.String(FlagWebsite, "[do-not-modify]", "optional website")
fsDescription.String(FlagDetails, "[do-not-modify]", "optional details")
fsValidator.String(FlagAddressValidator, "", "hex address of the validator")
fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator")
fsRedelegation.String(FlagAddressValidatorSrc, "", "hex address of the source validator")
fsRedelegation.String(FlagAddressValidatorDst, "", "hex address of the destination validator")
}

View File

@ -9,7 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/stake"
)
@ -105,14 +105,14 @@ func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
return cmd
}
// get the command to query a single delegation bond
// get the command to query a single delegation
func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegation",
Short: "Query a delegations bond based on address and validator address",
Short: "Query a delegation based on address and validator address",
RunE: func(cmd *cobra.Command, args []string) error {
addr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}
@ -122,26 +122,26 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
return err
}
key := stake.GetDelegationKey(delAddr, addr, cdc)
key := stake.GetDelegationKey(delAddr, valAddr, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}
// parse out the bond
bond := new(stake.Delegation)
// parse out the delegation
delegation := new(stake.Delegation)
switch viper.Get(cli.OutputFlag) {
case "text":
resp, err := bond.HumanReadableString()
resp, err := delegation.HumanReadableString()
if err != nil {
return err
}
fmt.Println(resp)
case "json":
cdc.MustUnmarshalBinary(res, bond)
output, err := wire.MarshalJSONIndent(cdc, bond)
cdc.MustUnmarshalBinary(res, delegation)
output, err := wire.MarshalJSONIndent(cdc, delegation)
if err != nil {
return err
}
@ -157,7 +157,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
return cmd
}
// get the command to query all the validators bonded to a delegation
// get the command to query all the delegations made from one delegator
func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegations [delegator-addr]",
@ -196,3 +196,190 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
}
return cmd
}
// get the command to query a single unbonding-delegation record
func GetCmdQueryUnbondingDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegation",
Short: "Query an unbonding-delegation record based on delegator and validator address",
RunE: func(cmd *cobra.Command, args []string) error {
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
if err != nil {
return err
}
key := stake.GetUBDKey(delAddr, valAddr, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}
// parse out the unbonding delegation
ubd := new(stake.UnbondingDelegation)
switch viper.Get(cli.OutputFlag) {
case "text":
resp, err := ubd.HumanReadableString()
if err != nil {
return err
}
fmt.Println(resp)
case "json":
cdc.MustUnmarshalBinary(res, ubd)
output, err := wire.MarshalJSONIndent(cdc, ubd)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
return nil
},
}
cmd.Flags().AddFlagSet(fsValidator)
cmd.Flags().AddFlagSet(fsDelegator)
return cmd
}
// get the command to query all the unbonding-delegation records for a delegator
func GetCmdQueryUnbondingDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegations [delegator-addr]",
Short: "Query all unbonding-delegations records for one delegator",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
if err != nil {
return err
}
key := stake.GetUBDsKey(delegatorAddr, cdc)
ctx := context.NewCoreContextFromViper()
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
if err != nil {
return err
}
// parse out the validators
var ubds []stake.UnbondingDelegation
for _, KV := range resKVs {
var ubd stake.UnbondingDelegation
cdc.MustUnmarshalBinary(KV.Value, &ubd)
ubds = append(ubds, ubd)
}
output, err := wire.MarshalJSONIndent(cdc, ubds)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
// TODO output with proofs / machine parseable etc.
},
}
return cmd
}
// get the command to query a single unbonding-delegation record
func GetCmdQueryRedelegation(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegation",
Short: "Query an unbonding-delegation record based on delegator and validator address",
RunE: func(cmd *cobra.Command, args []string) error {
valSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
if err != nil {
return err
}
valDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
if err != nil {
return err
}
key := stake.GetREDKey(delAddr, valSrcAddr, valDstAddr, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}
// parse out the unbonding delegation
red := new(stake.Redelegation)
switch viper.Get(cli.OutputFlag) {
case "text":
resp, err := red.HumanReadableString()
if err != nil {
return err
}
fmt.Println(resp)
case "json":
cdc.MustUnmarshalBinary(res, red)
output, err := wire.MarshalJSONIndent(cdc, red)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
return nil
},
}
cmd.Flags().AddFlagSet(fsRedelegation)
cmd.Flags().AddFlagSet(fsDelegator)
return cmd
}
// get the command to query all the unbonding-delegation records for a delegator
func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbonding-delegations [delegator-addr]",
Short: "Query all unbonding-delegations records for one delegator",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
if err != nil {
return err
}
key := stake.GetREDsKey(delegatorAddr, cdc)
ctx := context.NewCoreContextFromViper()
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
if err != nil {
return err
}
// parse out the validators
var reds []stake.Redelegation
for _, KV := range resKVs {
var red stake.Redelegation
cdc.MustUnmarshalBinary(KV.Value, &red)
reds = append(reds, red)
}
output, err := wire.MarshalJSONIndent(cdc, reds)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
// TODO output with proofs / machine parseable etc.
},
}
return cmd
}

View File

@ -3,6 +3,7 @@ package cli
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -104,11 +105,11 @@ func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
return cmd
}
// create edit validator command
// delegate command
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegate",
Short: "delegate coins to an existing validator",
Short: "delegate liquid tokens to an validator",
RunE: func(cmd *cobra.Command, args []string) error {
amount, err := sdk.ParseCoin(viper.GetString(FlagAmount))
if err != nil {
@ -143,33 +144,185 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
}
// create edit validator command
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
func GetCmdRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbond",
Short: "unbond shares from a validator",
Use: "redelegate",
Short: "redelegate illiquid tokens from one validator to another",
}
cmd.AddCommand(
GetCmdBeginRedelegate(storeName, cdc),
GetCmdCompleteRedelegate(cdc),
)
return cmd
}
// redelegate command
func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "begin",
Short: "begin redelegation",
RunE: func(cmd *cobra.Command, args []string) error {
// check the shares before broadcasting
sharesStr := viper.GetString(FlagShares)
var shares sdk.Rat
if sharesStr != "MAX" {
var err error
shares, err = sdk.NewRatFromDecimal(sharesStr)
if err != nil {
return err
}
if !shares.GT(sdk.ZeroRat()) {
return fmt.Errorf("shares must be positive integer or decimal (ex. 123, 1.23456789)")
}
var err error
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
if err != nil {
return err
}
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
if err != nil {
return err
}
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}
// get the shares amount
sharesAmountStr := viper.GetString(FlagSharesAmount)
sharesPercentStr := viper.GetString(FlagSharesPercent)
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
delegatorAddr, validatorSrcAddr)
if err != nil {
return err
}
msg := stake.NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr, sharesAmount)
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
cmd.Flags().AddFlagSet(fsShares)
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsRedelegation)
return cmd
}
func getShares(storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercentStr string,
delegatorAddr, validatorAddr sdk.Address) (sharesAmount sdk.Rat, err error) {
switch {
case sharesAmountStr != "" && sharesPercentStr != "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr == "" && sharesPercentStr == "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr != "":
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
if err != nil {
return sharesAmount, err
}
if !sharesAmount.GT(sdk.ZeroRat()) {
return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
}
case sharesPercentStr != "":
var sharesPercent sdk.Rat
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
if err != nil {
return sharesAmount, err
}
if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) {
return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
}
// make a query to get the existing delegation shares
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
ctx := context.NewCoreContextFromViper()
resQuery, err := ctx.QueryStore(key, storeName)
if err != nil {
return sharesAmount, err
}
var delegation stake.Delegation
err = cdc.UnmarshalBinary(resQuery, &delegation)
if err != nil {
return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err)
}
sharesAmount = sharesPercent.Mul(delegation.Shares)
}
return
}
// redelegate command
func GetCmdCompleteRedelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete redelegation",
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}
msg := stake.NewMsgCompleteRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr)
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsRedelegation)
return cmd
}
// create edit validator command
func GetCmdUnbond(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbond",
Short: "begin or complete unbonding shares from a validator",
}
cmd.AddCommand(
GetCmdBeginUnbonding(storeName, cdc),
GetCmdCompleteUnbonding(cdc),
)
return cmd
}
// create edit validator command
func GetCmdBeginUnbonding(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "begin",
Short: "begin unbonding",
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
if err != nil {
return err
}
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}
msg := stake.NewMsgUnbond(delegatorAddr, validatorAddr, sharesStr)
// get the shares amount
sharesAmountStr := viper.GetString(FlagSharesAmount)
sharesPercentStr := viper.GetString(FlagSharesPercent)
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
delegatorAddr, validatorAddr)
if err != nil {
return err
}
msg := stake.NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sharesAmount)
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
@ -189,3 +342,35 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
cmd.Flags().AddFlagSet(fsValidator)
return cmd
}
// create edit validator command
func GetCmdCompleteUnbonding(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete unbonding",
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}
msg := stake.NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
// build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
cmd.Flags().AddFlagSet(fsDelegator)
cmd.Flags().AddFlagSet(fsValidator)
return cmd
}

View File

@ -13,18 +13,30 @@ import (
)
func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
r.HandleFunc(
"/stake/{delegator}/bonding_status/{validator}",
bondingStatusHandlerFn(ctx, "stake", cdc),
"/stake/{delegator}/delegation/{validator}",
delegationHandlerFn(ctx, "stake", cdc),
).Methods("GET")
r.HandleFunc(
"/stake/{delegator}/ubd/{validator}",
ubdHandlerFn(ctx, "stake", cdc),
).Methods("GET")
r.HandleFunc(
"/stake/{delegator}/red/{validator_src}/{validator_dst}",
redHandlerFn(ctx, "stake", cdc),
).Methods("GET")
r.HandleFunc(
"/stake/validators",
validatorsHandlerFn(ctx, "stake", cdc),
).Methods("GET")
}
// http request handler to query delegator bonding status
func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
// http request handler to query a delegation
func delegationHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// read parameters
@ -51,25 +63,147 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
res, err := ctx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error())))
return
}
// the query will return empty if there is no data for this bond
// the query will return empty if there is no data for this record
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
return
}
var bond stake.Delegation
err = cdc.UnmarshalBinary(res, &bond)
var delegation stake.Delegation
err = cdc.UnmarshalBinary(res, &delegation)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode bond. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("couldn't decode delegation. Error: %s", err.Error())))
return
}
output, err := cdc.MarshalJSON(bond)
output, err := cdc.MarshalJSON(delegation)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}
// http request handler to query an unbonding-delegation
func ubdHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// read parameters
vars := mux.Vars(r)
bech32delegator := vars["delegator"]
bech32validator := vars["validator"]
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
key := stake.GetUBDKey(delegatorAddr, validatorAddr, cdc)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error())))
return
}
// the query will return empty if there is no data for this record
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
return
}
var ubd stake.UnbondingDelegation
err = cdc.UnmarshalBinary(res, &ubd)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode unbonding-delegation. Error: %s", err.Error())))
return
}
output, err := cdc.MarshalJSON(ubd)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}
// http request handler to query an redelegation
func redHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// read parameters
vars := mux.Vars(r)
bech32delegator := vars["delegator"]
bech32validatorSrc := vars["validator_src"]
bech32validatorDst := vars["validator_dst"]
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
validatorSrcAddr, err := sdk.GetValAddressBech32(bech32validatorSrc)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
validatorDstAddr, err := sdk.GetValAddressBech32(bech32validatorDst)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
key := stake.GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr, cdc)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query redelegation. Error: %s", err.Error())))
return
}
// the query will return empty if there is no data for this record
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
return
}
var red stake.Redelegation
err = cdc.UnmarshalBinary(res, &red)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode redelegation. Error: %s", err.Error())))
return
}
output, err := cdc.MarshalJSON(red)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))

View File

@ -24,31 +24,50 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k
).Methods("POST")
}
type msgDelegateInput struct {
type msgDelegationsInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
Bond sdk.Coin `json:"bond"`
}
type msgUnbondInput struct {
type msgBeginRedelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
SharesAmount string `json:"shares"`
}
type msgCompleteRedelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
}
type msgBeginUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
SharesAmount string `json:"shares"`
}
type msgCompleteUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
Shares string `json:"shares"`
}
type editDelegationsBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Delegate []msgDelegateInput `json:"delegate"`
Unbond []msgUnbondInput `json:"unbond"`
// request body for edit delegations
type EditDelegationsBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
}
func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m editDelegationsBody
var m EditDelegationsBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
@ -70,24 +89,29 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
}
// build messages
messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond))
messages := make([]sdk.Msg, len(m.Delegations)+
len(m.BeginRedelegates)+
len(m.CompleteRedelegates)+
len(m.BeginUnbondings)+
len(m.CompleteUnbondings))
i := 0
for _, msg := range m.Delegate {
for _, msg := range m.Delegations {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("must use own delegator address"))
w.Write([]byte("Must use own delegator address"))
return
}
messages[i] = stake.MsgDelegate{
@ -97,28 +121,131 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
}
i++
}
for _, msg := range m.Unbond {
for _, msg := range m.BeginRedelegates {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Must use own delegator address"))
return
}
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
return
}
messages[i] = stake.MsgBeginRedelegate{
DelegatorAddr: delegatorAddr,
ValidatorSrcAddr: validatorSrcAddr,
ValidatorDstAddr: validatorDstAddr,
SharesAmount: shares,
}
i++
}
for _, msg := range m.CompleteRedelegates {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Must use own delegator address"))
return
}
messages[i] = stake.MsgCompleteRedelegate{
DelegatorAddr: delegatorAddr,
ValidatorSrcAddr: validatorSrcAddr,
ValidatorDstAddr: validatorDstAddr,
}
i++
}
for _, msg := range m.BeginUnbondings {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Must use own delegator address"))
return
}
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
return
}
messages[i] = stake.MsgBeginUnbonding{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
SharesAmount: shares,
}
i++
}
for _, msg := range m.CompleteUnbondings {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("must use own delegator address"))
w.Write([]byte("Must use own delegator address"))
return
}
messages[i] = stake.MsgUnbond{
messages[i] = stake.MsgCompleteUnbonding{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Shares: msg.Shares,
}
i++
}

View File

@ -1,52 +0,0 @@
package stake
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// pubKey.
type Delegation struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"validator_addr"`
Shares sdk.Rat `json:"shares"`
Height int64 `json:"height"` // Last height bond updated
}
func (b Delegation) equal(b2 Delegation) bool {
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
bytes.Equal(b.ValidatorAddr, b2.ValidatorAddr) &&
b.Height == b2.Height &&
b.Shares.Equal(b2.Shares)
}
// ensure fulfills the sdk validator types
var _ sdk.Delegation = Delegation{}
// nolint - for sdk.Delegation
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
func (b Delegation) GetValidator() sdk.Address { return b.ValidatorAddr }
func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
//Human Friendly pretty printer
func (b Delegation) HumanReadableString() (string, error) {
bechAcc, err := sdk.Bech32ifyAcc(b.DelegatorAddr)
if err != nil {
return "", err
}
bechVal, err := sdk.Bech32ifyAcc(b.ValidatorAddr)
if err != nil {
return "", err
}
resp := "Delegation \n"
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
resp += fmt.Sprintf("Validator: %s\n", bechVal)
resp += fmt.Sprintf("Shares: %s", b.Shares.String())
resp += fmt.Sprintf("Height: %d", b.Height)
return resp, nil
}

View File

@ -1,117 +0,0 @@
// nolint
package stake
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type CodeType = sdk.CodeType
const (
DefaultCodespace sdk.CodespaceType = 4
// Gaia errors reserve 200 ~ 299.
CodeInvalidValidator CodeType = 201
CodeInvalidBond CodeType = 202
CodeInvalidInput CodeType = 203
CodeValidatorJailed CodeType = 204
CodeUnauthorized CodeType = sdk.CodeUnauthorized
CodeInternal CodeType = sdk.CodeInternal
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
)
// NOTE: Don't stringer this, we'll put better messages in later.
func codeToDefaultMsg(code CodeType) string {
switch code {
case CodeInvalidValidator:
return "invalid Validator"
case CodeInvalidBond:
return "invalid Bond"
case CodeInvalidInput:
return "invalid Input"
case CodeUnauthorized:
return "unauthorized"
case CodeInternal:
return "internal Error"
case CodeUnknownRequest:
return "unknown request"
default:
return sdk.CodeToDefaultMsg(code)
}
}
//----------------------------------------
// Error constructors
func ErrNotEnoughBondShares(codespace sdk.CodespaceType, shares string) sdk.Error {
return newError(codespace, CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
}
func ErrValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "cannot bond to an empty validator")
}
func ErrBadBondingDenom(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidBond, "invalid coin denomination")
}
func ErrBadBondingAmount(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidBond, "amount must be > 0")
}
func ErrNoBondingAcct(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "no bond account for this (address, validator) pair")
}
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "commission must be positive")
}
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
}
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
}
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "delegator does not exist for that address")
}
func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator")
}
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "validator for this address is currently revoked")
}
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "missing signature")
}
func ErrBondNotNominated(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "cannot bond to non-nominated account")
}
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
}
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "delegator does not contain validator bond")
}
func ErrInsufficientFunds(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidInput, "insufficient bond shares")
}
func ErrBadShares(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidInput, "bad shares provided as input, must be MAX or decimal")
}
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "error removing validator")
}
//----------------------------------------
// TODO group with code from x/bank/errors.go
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
}
return codeToDefaultMsg(code)
}
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(codespace, code, msg)
}

View File

@ -4,63 +4,39 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// GenesisState - all staking state that must be provided at genesis
type GenesisState struct {
Pool Pool `json:"pool"`
Params Params `json:"params"`
Validators []Validator `json:"validators"`
Bonds []Delegation `json:"bonds"`
}
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
return GenesisState{
Pool: pool,
Params: params,
Validators: validators,
Bonds: bonds,
}
}
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
return GenesisState{
Pool: InitialPool(),
Params: DefaultParams(),
}
}
// InitGenesis - store genesis parameters
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
store := ctx.KVStore(k.storeKey)
k.setPool(ctx, data.Pool)
k.setNewParams(ctx, data.Params)
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
keeper.SetPool(ctx, data.Pool)
keeper.SetNewParams(ctx, data.Params)
keeper.InitIntraTxCounter(ctx)
for _, validator := range data.Validators {
// set validator
k.setValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
// manually set indexes for the first time
k.setValidatorByPubKeyIndex(ctx, validator)
k.setValidatorByPowerIndex(ctx, validator, data.Pool)
keeper.SetValidatorByPubKeyIndex(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
if validator.Status() == sdk.Bonded {
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
keeper.SetValidatorBondedIndex(ctx, validator)
}
}
for _, bond := range data.Bonds {
k.setDelegation(ctx, bond)
keeper.SetDelegation(ctx, bond)
}
k.updateBondedValidatorsFull(ctx, store)
keeper.UpdateBondedValidatorsFull(ctx)
}
// WriteGenesis - output genesis parameters
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
pool := k.GetPool(ctx)
params := k.GetParams(ctx)
validators := k.getAllValidators(ctx)
bonds := k.getAllDelegations(ctx)
return GenesisState{
func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
pool := keeper.GetPool(ctx)
params := keeper.GetParams(ctx)
validators := keeper.GetAllValidators(ctx)
bonds := keeper.GetAllDelegations(ctx)
return types.GenesisState{
pool,
params,
validators,
@ -69,8 +45,8 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
}
// WriteValidators - output current validator set
func WriteValidators(ctx sdk.Context, k Keeper) (vals []tmtypes.GenesisValidator) {
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
vals = append(vals, tmtypes.GenesisValidator{
PubKey: validator.GetPubKey(),
Power: validator.GetPower().Evaluate(),

View File

@ -1,24 +1,32 @@
package stake
import (
"bytes"
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
"github.com/cosmos/cosmos-sdk/x/stake/tags"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
func NewHandler(k Keeper) sdk.Handler {
func NewHandler(k keeper.Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// NOTE msg already has validate basic run
switch msg := msg.(type) {
case MsgCreateValidator:
case types.MsgCreateValidator:
return handleMsgCreateValidator(ctx, msg, k)
case MsgEditValidator:
case types.MsgEditValidator:
return handleMsgEditValidator(ctx, msg, k)
case MsgDelegate:
case types.MsgDelegate:
return handleMsgDelegate(ctx, msg, k)
case MsgUnbond:
return handleMsgUnbond(ctx, msg, k)
case types.MsgBeginRedelegate:
return handleMsgBeginRedelegate(ctx, msg, k)
case types.MsgCompleteRedelegate:
return handleMsgCompleteRedelegate(ctx, msg, k)
case types.MsgBeginUnbonding:
return handleMsgBeginUnbonding(ctx, msg, k)
case types.MsgCompleteUnbonding:
return handleMsgCompleteUnbonding(ctx, msg, k)
default:
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
}
@ -26,25 +34,25 @@ func NewHandler(k Keeper) sdk.Handler {
}
// Called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
pool := k.GetPool(ctx)
// Process Validator Provisions
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
// Process types.Validator Provisions
blockTime := ctx.BlockHeader().Time
if pool.InflationLastTime+blockTime >= 3600 {
pool.InflationLastTime = blockTime
pool = k.processProvisions(ctx)
pool = k.ProcessProvisions(ctx)
}
// save the params
k.setPool(ctx, pool)
k.SetPool(ctx, pool)
// reset the intra-transaction counter
k.setIntraTxCounter(ctx, 0)
k.SetIntraTxCounter(ctx, 0)
// calculate validator set changes
ValidatorUpdates = k.getTendermintUpdates(ctx)
k.clearTendermintUpdates(ctx)
ValidatorUpdates = k.GetTendermintUpdates(ctx)
k.ClearTendermintUpdates(ctx)
return
}
@ -53,212 +61,150 @@ func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
// These functions assume everything has been authenticated,
// now we just perform action and save
func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k Keeper) sdk.Result {
func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result {
// check to see if the pubkey or sender has been registered before
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
if found {
return ErrValidatorExistsAddr(k.codespace).Result()
return ErrValidatorAlreadyExists(k.Codespace()).Result()
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
if msg.SelfDelegation.Denom != k.GetParams(ctx).BondDenom {
return ErrBadDenom(k.Codespace()).Result()
}
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
k.setValidator(ctx, validator)
k.setValidatorByPubKeyIndex(ctx, validator)
tags := sdk.NewTags(
"action", []byte("createValidator"),
"validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity),
)
k.SetValidator(ctx, validator)
k.SetValidatorByPubKeyIndex(ctx, validator)
// move coins from the msg.Address account to a (self-bond) delegator account
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
delegateTags, err := delegate(ctx, k, msg.ValidatorAddr, msg.Bond, validator)
_, err := k.Delegate(ctx, msg.ValidatorAddr, msg.SelfDelegation, validator)
if err != nil {
return err.Result()
}
tags = tags.AppendTags(delegateTags)
tags := sdk.NewTags(
tags.Action, tags.ActionCreateValidator,
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
tags.Moniker, []byte(msg.Description.Moniker),
tags.Identity, []byte(msg.Description.Identity),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgEditValidator(ctx sdk.Context, msg MsgEditValidator, k Keeper) sdk.Result {
func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result {
// validator must already be registered
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
if !found {
return ErrBadValidatorAddr(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
return ErrNoValidatorFound(k.Codespace()).Result()
}
// XXX move to types
// replace all editable fields (clients should autofill existing values)
validator.Description.Moniker = msg.Description.Moniker
validator.Description.Identity = msg.Description.Identity
validator.Description.Website = msg.Description.Website
validator.Description.Details = msg.Description.Details
description, err := validator.Description.UpdateDescription(msg.Description)
if err != nil {
return err.Result()
}
validator.Description = description
k.updateValidator(ctx, validator)
k.UpdateValidator(ctx, validator)
tags := sdk.NewTags(
"action", []byte("editValidator"),
"validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity),
tags.Action, tags.ActionEditValidator,
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
tags.Moniker, []byte(description.Moniker),
tags.Identity, []byte(description.Identity),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result {
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
if !found {
return ErrBadValidatorAddr(k.codespace).Result()
return ErrNoValidatorFound(k.Codespace()).Result()
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom(k.codespace).Result()
return ErrBadDenom(k.Codespace()).Result()
}
if validator.Revoked == true {
return ErrValidatorRevoked(k.codespace).Result()
return ErrValidatorRevoked(k.Codespace()).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
}
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, validator)
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Bond, validator)
if err != nil {
return err.Result()
}
tags := sdk.NewTags(
tags.Action, tags.ActionDelegate,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
)
return sdk.Result{
Tags: tags,
}
}
// common functionality between handlers
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
bondAmt sdk.Coin, validator Validator) (sdk.Tags, sdk.Error) {
// Get or create the delegator bond
bond, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
if !found {
bond = Delegation{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validator.Owner,
Shares: sdk.ZeroRat(),
}
}
// Account new shares, save
pool := k.GetPool(ctx)
_, _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt})
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
if err != nil {
return nil, err
return err.Result()
}
validator, pool, newShares := validator.addTokensFromDel(pool, bondAmt.Amount)
bond.Shares = bond.Shares.Add(newShares)
// Update bond height
bond.Height = ctx.BlockHeight()
k.setPool(ctx, pool)
k.setDelegation(ctx, bond)
k.updateValidator(ctx, validator)
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "validator", validator.Owner.Bytes())
return tags, nil
tags := sdk.NewTags(
tags.Action, tags.ActionBeginUnbonding,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
)
return sdk.Result{Tags: tags}
}
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
func handleMsgCompleteUnbonding(ctx sdk.Context, msg types.MsgCompleteUnbonding, k keeper.Keeper) sdk.Result {
// check if bond has any shares in it unbond
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
if !found {
return ErrNoDelegatorForAddress(k.codespace).Result()
err := k.CompleteUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
if err != nil {
return err.Result()
}
var delShares sdk.Rat
tags := sdk.NewTags(
tags.Action, ActionCompleteUnbonding,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
)
// test that there are enough shares to unbond
if msg.Shares == "MAX" {
if !bond.Shares.GT(sdk.ZeroRat()) {
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
}
} else {
var err sdk.Error
delShares, err = sdk.NewRatFromDecimal(msg.Shares)
if err != nil {
return err.Result()
}
if bond.Shares.LT(delShares) {
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
}
}
// get validator
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
if !found {
return ErrNoValidatorForAddress(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
}
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
if msg.Shares == "MAX" {
delShares = bond.Shares
}
// subtract bond tokens from delegator bond
bond.Shares = bond.Shares.Sub(delShares)
// remove the bond
revokeValidator := false
if bond.Shares.IsZero() {
// if the bond is the owner of the validator then
// trigger a revoke validator
if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
validator.Revoked == false {
revokeValidator = true
}
k.removeDelegation(ctx, bond)
} else {
// Update bond height
bond.Height = ctx.BlockHeight()
k.setDelegation(ctx, bond)
}
// Add the coins
pool := k.GetPool(ctx)
validator, pool, returnAmount := validator.removeDelShares(pool, delShares)
k.setPool(ctx, pool)
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
/////////////////////////////////////
// revoke validator if necessary
if revokeValidator {
validator.Revoked = true
}
validator = k.updateValidator(ctx, validator)
if validator.DelegatorShares.IsZero() {
k.removeValidator(ctx, validator.Owner)
}
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes())
return sdk.Result{
Tags: tags,
}
return sdk.Result{Tags: tags}
}
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
msg.ValidatorDstAddr, msg.SharesAmount)
if err != nil {
return err.Result()
}
tags := sdk.NewTags(
tags.Action, tags.ActionBeginRedelegation,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
)
return sdk.Result{Tags: tags}
}
func handleMsgCompleteRedelegate(ctx sdk.Context, msg types.MsgCompleteRedelegate, k keeper.Keeper) sdk.Result {
err := k.CompleteRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr, msg.ValidatorDstAddr)
if err != nil {
return err.Result()
}
tags := sdk.NewTags(
tags.Action, tags.ActionCompleteRedelegation,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
)
return sdk.Result{Tags: tags}
}

View File

@ -1,7 +1,6 @@
package stake
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
@ -10,38 +9,48 @@ import (
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
keep "github.com/cosmos/cosmos-sdk/x/stake/keeper"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
//______________________________________________________________________
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
return MsgCreateValidator{
Description: Description{},
ValidatorAddr: address,
PubKey: pubKey,
Bond: sdk.Coin{"steak", amt},
Description: Description{},
ValidatorAddr: address,
PubKey: pubKey,
SelfDelegation: sdk.Coin{"steak", sdk.NewInt(amt)},
}
}
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt sdk.Int) MsgDelegate {
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Bond: sdk.Coin{"steak", amt},
Bond: sdk.Coin{"steak", sdk.NewInt(amt)},
}
}
// retrieve params which are instant
func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params {
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
keeper.SetParams(ctx, params)
return params
}
//______________________________________________________________________
func TestValidatorByPowerIndex(t *testing.T) {
validatorAddr, validatorAddr3 := addrs[0], addrs[1]
validatorAddr, validatorAddr3 := keep.Addrs[0], keep.Addrs[1]
initBond := sdk.NewInt(1000000)
initBondStr := "1000"
ctx, _, keeper := createTestInput(t, false, initBond)
initBond := int64(1000000)
ctx, _, keeper := keep.CreateTestInput(t, false, initBond)
_ = setInstantUnbondPeriod(keeper, ctx)
// create validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
@ -49,7 +58,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
require.True(t, found)
gotBond := bond.Shares.Evaluate()
require.Equal(t, initBond.Int64(), gotBond,
require.Equal(t, initBond, gotBond,
"initBond: %v\ngotBond: %v\nbond: %v\n",
initBond, gotBond, bond)
@ -57,60 +66,62 @@ func TestValidatorByPowerIndex(t *testing.T) {
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
pool := keeper.GetPool(ctx)
power := GetValidatorsByPowerKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
power := keep.GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
// create a second validator keep it bonded
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, pks[2], sdk.NewInt(1000000))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000))
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// slash and revoke the first validator
keeper.Slash(ctx, pks[0], 0, sdk.NewRat(1, 2))
keeper.Revoke(ctx, pks[0])
keeper.Slash(ctx, keep.PKs[0], 0, sdk.NewRat(1, 2))
keeper.Revoke(ctx, keep.PKs[0])
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded
require.Equal(t, int64(500000), validator.PoolShares.Amount.Evaluate()) // ensure is unbonded
// the old power record should have been deleted as the power changed
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
// but the new power record should have been created
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
pool = keeper.GetPool(ctx)
power2 := GetValidatorsByPowerKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power2))
power2 := GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
// inflate a bunch
for i := 0; i < 20000; i++ {
pool = keeper.processProvisions(ctx)
keeper.setPool(ctx, pool)
pool = keeper.ProcessProvisions(ctx)
keeper.SetPool(ctx, pool)
}
// now the new record power index should be the same as the original record
power3 := GetValidatorsByPowerKey(validator, pool)
power3 := GetValidatorsByPowerIndexKey(validator, pool)
assert.Equal(t, power2, power3)
// unbond self-delegation
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "MAX")
got = handleMsgUnbond(ctx, msgUnbond, keeper)
assert.True(t, got.IsOK(),
"got: %v\nmsgUnbond: %v\ninitBondStr: %v\n", got, msgUnbond, initBondStr)
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(1000000))
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
// verify that by power key nolonger exists
_, found = keeper.GetValidator(ctx, validatorAddr)
require.False(t, found)
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power3))
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power3))
}
func TestDuplicatesMsgCreateValidator(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr := addrs[0]
pk := pks[0]
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, sdk.NewInt(10))
validatorAddr := keep.Addrs[0]
pk := keep.PKs[0]
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "%v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr)
@ -124,41 +135,41 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
assert.Equal(t, Description{}, validator.Description)
// one validator cannot bond twice
msgCreateValidator.PubKey = pks[1]
msgCreateValidator.PubKey = keep.PKs[1]
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.False(t, got.IsOK(), "%v", got)
}
func TestIncrementsMsgDelegate(t *testing.T) {
initBond := sdk.NewInt(1000)
ctx, accMapper, keeper := createTestInput(t, false, initBond)
initBond := int64(1000)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
params := keeper.GetParams(ctx)
bondAmount := sdk.NewInt(10)
validatorAddr, delegatorAddr := addrs[0], addrs[1]
bondAmount := int64(10)
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
// first create validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], bondAmount)
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status())
assert.Equal(t, bondAmount, validator.DelegatorShares.EvaluateInt())
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().EvaluateInt(), "validator: %v", validator)
assert.Equal(t, bondAmount, validator.DelegatorShares.Evaluate())
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().Evaluate(), "validator: %v", validator)
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
require.False(t, found)
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
require.True(t, found)
assert.Equal(t, bondAmount, bond.Shares.EvaluateInt())
assert.Equal(t, bondAmount, bond.Shares.Evaluate())
pool := keeper.GetPool(ctx)
exRate := validator.DelegatorShareExRate(pool)
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
assert.Equal(t, bondAmount, pool.BondedShares.EvaluateInt())
assert.Equal(t, bondAmount, pool.BondedShares.Evaluate())
assert.Equal(t, bondAmount, pool.BondedTokens)
// just send the same msgbond multiple times
@ -180,14 +191,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
exRate := validator.DelegatorShareExRate(pool)
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
expBond := sdk.NewInt(int64(i + 1)).Mul(bondAmount)
expDelegatorShares := sdk.NewInt(int64(i + 2)).Mul(bondAmount) // (1 self delegation)
expDelegatorAcc := initBond.Sub(expBond)
expBond := int64(i+1) * bondAmount
expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation)
expDelegatorAcc := sdk.NewInt(initBond - expBond)
require.Equal(t, bond.Height, int64(i), "Incorrect bond height")
gotBond := bond.Shares.EvaluateInt()
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
gotBond := bond.Shares.Evaluate()
gotDelegatorShares := validator.DelegatorShares.Evaluate()
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
require.Equal(t, expBond, gotBond,
@ -203,14 +214,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
}
func TestIncrementsMsgUnbond(t *testing.T) {
initBond := sdk.NewInt(1000)
ctx, accMapper, keeper := createTestInput(t, false, initBond)
params := keeper.GetParams(ctx)
initBond := int64(1000)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
params := setInstantUnbondPeriod(keeper, ctx)
// create validator, delegate
validatorAddr, delegatorAddr := addrs[0], addrs[1]
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
@ -220,16 +231,19 @@ func TestIncrementsMsgUnbond(t *testing.T) {
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
assert.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.EvaluateInt())
assert.Equal(t, initBond.MulRaw(2), validator.PoolShares.Bonded().EvaluateInt())
assert.Equal(t, initBond*2, validator.DelegatorShares.Evaluate())
assert.Equal(t, initBond*2, validator.PoolShares.Bonded().Evaluate())
// just send the same msgUnbond multiple times
// TODO use decimals here
unbondShares, unbondSharesStr := int64(10), "10"
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
unbondShares := sdk.NewRat(10)
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
numUnbonds := 5
for i := 0; i < numUnbonds; i++ {
got := handleMsgUnbond(ctx, msgUnbond, keeper)
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the accounts and the bond account have the appropriate values
@ -238,12 +252,12 @@ func TestIncrementsMsgUnbond(t *testing.T) {
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
require.True(t, found)
expBond := initBond.SubRaw(int64(i+1) * unbondShares)
expDelegatorShares := initBond.MulRaw(2).SubRaw(int64(i+1) * unbondShares)
expDelegatorAcc := initBond.Sub(expBond)
expBond := initBond - int64(i+1)*unbondShares.Evaluate()
expDelegatorShares := 2*initBond - int64(i+1)*unbondShares.Evaluate()
expDelegatorAcc := sdk.NewInt(initBond - expBond)
gotBond := bond.Shares.EvaluateInt()
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
gotBond := bond.Shares.Evaluate()
gotDelegatorShares := validator.DelegatorShares.Evaluate()
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
require.Equal(t, expBond, gotBond,
@ -263,41 +277,42 @@ func TestIncrementsMsgUnbond(t *testing.T) {
//1<<63 + 1, // more than int64
1<<63 - 1,
1 << 31,
initBond.Int64(),
initBond,
}
for _, c := range errorCases {
unbondShares := strconv.Itoa(int(c))
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondShares)
got = handleMsgUnbond(ctx, msgUnbond, keeper)
unbondShares := sdk.NewRat(int64(c))
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.False(t, got.IsOK(), "expected unbond msg to fail")
}
leftBonded := initBond.SubRaw(unbondShares * int64(numUnbonds))
leftBonded := initBond - int64(numUnbonds)*unbondShares.Evaluate()
// should be unable to unbond one more than we have
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()) + 1)
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
got = handleMsgUnbond(ctx, msgUnbond, keeper)
unbondShares = sdk.NewRat(leftBonded + 1)
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
assert.False(t, got.IsOK(),
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares.String(), leftBonded)
// should be able to unbond just what we have
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()))
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
got = handleMsgUnbond(ctx, msgUnbond, keeper)
unbondShares = sdk.NewRat(leftBonded)
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
assert.True(t, got.IsOK(),
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares, leftBonded)
}
func TestMultipleMsgCreateValidator(t *testing.T) {
initBond := sdk.NewInt(1000)
ctx, accMapper, keeper := createTestInput(t, false, initBond)
params := keeper.GetParams(ctx)
validatorAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
initBond := int64(1000)
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
params := setInstantUnbondPeriod(keeper, ctx)
validatorAddrs := []sdk.Address{keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]}
// bond them all
for i, validatorAddr := range validatorAddrs {
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[i], sdk.NewInt(10))
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[i], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
@ -305,7 +320,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
validators := keeper.GetValidators(ctx, 100)
require.Equal(t, (i + 1), len(validators))
val := validators[i]
balanceExpd := initBond.SubRaw(10)
balanceExpd := sdk.NewInt(initBond - 10)
balanceGot := accMapper.GetAccount(ctx, val.Owner).GetCoins().AmountOf(params.BondDenom)
require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators)
require.Equal(t, 10, int(val.DelegatorShares.Evaluate()), "expected %d shares, got %d", 10, val.DelegatorShares)
@ -316,8 +331,11 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
for i, validatorAddr := range validatorAddrs {
validatorPre, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "10") // self-delegation
got := handleMsgUnbond(ctx, msgUnbond, keeper)
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) // self-delegation
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
@ -328,24 +346,25 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
_, found = keeper.GetValidator(ctx, validatorAddr)
require.False(t, found)
expBalance := initBond
expBalance := sdk.NewInt(initBond)
gotBalance := accMapper.GetAccount(ctx, validatorPre.Owner).GetCoins().AmountOf(params.BondDenom)
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
}
}
func TestMultipleMsgDelegate(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
validatorAddr, delegatorAddrs := addrs[0], addrs[1:]
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddrs := keep.Addrs[0], keep.Addrs[1:]
_ = setInstantUnbondPeriod(keeper, ctx)
//first make a validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
// delegate multiple parties
for i, delegatorAddr := range delegatorAddrs {
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got := handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
@ -357,8 +376,11 @@ func TestMultipleMsgDelegate(t *testing.T) {
// unbond them all
for i, delegatorAddr := range delegatorAddrs {
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
got := handleMsgUnbond(ctx, msgUnbond, keeper)
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
@ -368,37 +390,173 @@ func TestMultipleMsgDelegate(t *testing.T) {
}
func TestRevokeValidator(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
validatorAddr, delegatorAddr := addrs[0], addrs[1]
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
_ = setInstantUnbondPeriod(keeper, ctx)
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
validator, _ := keeper.GetValidator(ctx, validatorAddr)
// unbond the validators bond portion
msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10")
got = handleMsgUnbond(ctx, msgUnbondValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.True(t, validator.Revoked)
require.True(t, validator.Revoked, "%v", validator)
// test that this address cannot yet be bonded too because is revoked
got = handleMsgDelegate(ctx, msgDelegate, keeper)
assert.False(t, got.IsOK(), "expected error, got %v", got)
// test that the delegator can still withdraw their bonds
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
// verify that the pubkey can now be reused
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected ok, got %v", got)
}
func TestUnbondingPeriod(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr := keep.Addrs[0]
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 7
keeper.SetParams(ctx, params)
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// begin unbonding
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error")
// cannot complete unbonding at same time
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected no error")
// cannot complete unbonding at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time += 6
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected no error")
// can complete unbonding at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time += 7
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error")
}
func TestRedelegationPeriod(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr, validatorAddr2 := keep.Addrs[0], keep.Addrs[1]
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 7
keeper.SetParams(ctx, params)
// create the validators
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// begin redelegate
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
// cannot complete redelegation at same time
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error")
// cannot complete redelegation at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time += 6
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error")
// can complete redelegation at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time += 7
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
}
func TestTransitiveRedelegation(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr, validatorAddr2, validatorAddr3 := keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
keeper.SetParams(ctx, params)
// create the validators
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// begin redelegate
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
// cannot redelegation to next validator while first delegation exists
msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewRat(10))
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
// complete first redelegation
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
// now should be able to redelegate from the second validator to the third
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
}

View File

@ -1,859 +0,0 @@
package stake
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
)
// keeper of the staking store
type Keeper struct {
storeKey sdk.StoreKey
cdc *wire.Codec
coinKeeper bank.Keeper
// codespace
codespace sdk.CodespaceType
}
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
coinKeeper: ck,
codespace: codespace,
}
return keeper
}
//_________________________________________________________________________
// get a single validator
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator Validator, found bool) {
store := ctx.KVStore(k.storeKey)
return k.getValidator(store, addr)
}
// get a single validator by pubkey
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator Validator, found bool) {
store := ctx.KVStore(k.storeKey)
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
if addr == nil {
return validator, false
}
return k.getValidator(store, addr)
}
// get a single validator (reuse store)
func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) {
b := store.Get(GetValidatorKey(addr))
if b == nil {
return validator, false
}
k.cdc.MustUnmarshalBinary(b, &validator)
return validator, true
}
// set the main record holding validator details
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey)
// set main store
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bz)
}
func (k Keeper) setValidatorByPubKeyIndex(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey)
// set pointer by pubkey
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
}
func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, pool Pool) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner)
}
// used in testing
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
store := ctx.KVStore(k.storeKey)
return store.Get(power) != nil
}
// Get the set of all validators with no limits, used during genesis dump
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
i := 0
for ; ; i++ {
if !iterator.Valid() {
iterator.Close()
break
}
bz := iterator.Value()
var validator Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
validators = append(validators, validator)
iterator.Next()
}
return validators
}
// Get the set of all validators, retrieve a maxRetrieve number of records
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators Validators) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
validators = make([]Validator, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bz := iterator.Value()
var validator Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
validators[i] = validator
iterator.Next()
}
return validators[:i] // trim
}
//___________________________________________________________________________
// get the group of the bonded validators
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []Validator) {
store := ctx.KVStore(k.storeKey)
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
validators = make([]Validator, maxValidators)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
i := 0
for ; iterator.Valid(); iterator.Next() {
// sanity check
if i > int(maxValidators-1) {
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
}
address := iterator.Value()
validator, found := k.getValidator(store, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
validators[i] = validator
i++
}
iterator.Close()
return validators[:i] // trim
}
// get the group of bonded validators sorted by power-rank
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []Validator {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
validators := make([]Validator, maxValidators)
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
i := 0
for {
if !iterator.Valid() || i > int(maxValidators-1) {
iterator.Close()
break
}
address := iterator.Value()
validator, found := k.getValidator(store, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
// Reached to revoked validators, stop iterating
if validator.Revoked {
iterator.Close()
break
}
if validator.Status() == sdk.Bonded {
validators[i] = validator
i++
}
iterator.Next()
}
return validators[:i] // trim
}
//_________________________________________________________________________
// Accumulated updates to the active/bonded validator set for tendermint
// get the most recently updated validators
func (k Keeper) getTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
for ; iterator.Valid(); iterator.Next() {
valBytes := iterator.Value()
var val abci.Validator
k.cdc.MustUnmarshalBinary(valBytes, &val)
updates = append(updates, val)
}
iterator.Close()
return
}
// remove all validator update entries after applied to Tendermint
func (k Keeper) clearTendermintUpdates(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//___________________________________________________________________________
// perfom all the nessisary steps for when a validator changes its power
// updates all validator stores as well as tendermint update store
// may kick out validators if new validator is entering the bonded validator group
func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator {
store := ctx.KVStore(k.storeKey)
pool := k.getPool(store)
ownerAddr := validator.Owner
// always update the main list ordered by owner address before exiting
defer func() {
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(ownerAddr), bz)
}()
// retreive the old validator record
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
validator = k.unbondValidator(ctx, store, validator)
// need to also clear the cliff validator spot because the revoke has
// opened up a new spot which will be filled when
// updateValidatorsBonded is called
k.clearCliffValidator(ctx)
}
powerIncreasing := false
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
powerIncreasing = true
}
// if already a validator, copy the old block height and counter, else set them
if oldFound && oldValidator.Status() == sdk.Bonded {
validator.BondHeight = oldValidator.BondHeight
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
} else {
validator.BondHeight = ctx.BlockHeight()
counter := k.getIntraTxCounter(ctx)
validator.BondIntraTxCounter = counter
k.setIntraTxCounter(ctx, counter+1)
}
// update the list ordered by voting power
if oldFound {
store.Delete(GetValidatorsByPowerKey(oldValidator, pool))
}
valPower := GetValidatorsByPowerKey(validator, pool)
store.Set(valPower, validator.Owner)
// efficiency case:
// if already bonded and power increasing only need to update tendermint
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
bz := k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
return validator
}
// efficiency case:
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
cliffPower := k.getCliffValidatorPower(ctx)
if cliffPower != nil &&
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
return validator
}
// update the validator set for this validator
updatedVal := k.updateBondedValidators(ctx, store, validator)
if updatedVal.Owner != nil { // updates to validator occured to be updated
validator = updatedVal
}
return validator
}
// Update the validator group and kick out any old validators. In addition this
// function adds (or doesn't add) a validator which has updated its bonded
// tokens to the validator group. -> this validator is specified through the
// updatedValidatorAddr term.
//
// The correct subset is retrieved by iterating through an index of the
// validators sorted by power, stored using the ValidatorsByPowerKey.
// Simultaneously the current validator records are updated in store with the
// ValidatorsBondedKey. This store is used to determine if a validator is a
// validator without needing to iterate over the subspace as we do in
// GetValidators.
//
// Optionally also return the validator from a retrieve address if the validator has been bonded
func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore,
newValidator Validator) (updatedVal Validator) {
kickCliffValidator := false
oldCliffValidatorAddr := k.getCliffValidator(ctx)
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
bondedValidatorsCount := 0
var validator Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
// TODO benchmark if we should read the current power and not write if it's the same
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
}
iterator.Close()
break
}
// either retrieve the original validator from the store, or under the
// situation that this is the "new validator" just use the validator
// provided because it has not yet been updated in the main validator
// store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator
} else {
var found bool
validator, found = k.getValidator(store, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
}
// if not previously a validator (and unrevoked),
// kick the cliff validator / bond this new validator
if validator.Status() != sdk.Bonded && !validator.Revoked {
kickCliffValidator = true
validator = k.bondValidator(ctx, store, validator)
if bytes.Equal(ownerAddr, newValidator.Owner) {
updatedVal = validator
}
}
if validator.Revoked && validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
} else {
bondedValidatorsCount++
}
iterator.Next()
}
// perform the actual kicks
if oldCliffValidatorAddr != nil && kickCliffValidator {
validator, found := k.getValidator(store, oldCliffValidatorAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
}
k.unbondValidator(ctx, store, validator)
}
return
}
// full update of the bonded validator set, many can be added/kicked
func (k Keeper) updateBondedValidatorsFull(ctx sdk.Context, store sdk.KVStore) {
// clear the current validators store, add to the ToKickOut temp store
toKickOut := make(map[string]byte)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
for ; iterator.Valid(); iterator.Next() {
ownerAddr := iterator.Value()
toKickOut[string(ownerAddr)] = 0 // set anything
}
iterator.Close()
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
bondedValidatorsCount := 0
var validator Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
}
iterator.Close()
break
}
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
ownerAddr := iterator.Value()
var found bool
validator, found = k.getValidator(store, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
_, found = toKickOut[string(ownerAddr)]
if found {
delete(toKickOut, string(ownerAddr))
} else {
// if it wasn't in the toKickOut group it means
// this wasn't a previously a validator, therefor
// update the validator to enter the validator group
validator = k.bondValidator(ctx, store, validator)
}
if validator.Revoked && validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
} else {
bondedValidatorsCount++
}
iterator.Next()
}
// perform the actual kicks
for key := range toKickOut {
ownerAddr := []byte(key)
validator, found := k.getValidator(store, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
k.unbondValidator(ctx, store, validator)
}
return
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator {
pool := k.GetPool(ctx)
// sanity check
if validator.Status() == sdk.Unbonded {
panic(fmt.Sprintf("should not already be be unbonded, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
k.setPool(ctx, pool)
// save the now unbonded validator record
bzVal := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bzVal)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc))
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
// also remove from the Bonded Validators Store
store.Delete(GetValidatorsBondedKey(validator.PubKey))
return validator
}
// perform all the store operations for when a validator status becomes bonded
func (k Keeper) bondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator {
pool := k.GetPool(ctx)
// sanity check
if validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("should not already be be bonded, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
k.setPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
bzVal := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bzVal)
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
return validator
}
func (k Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
// first retreive the old validator record
validator, found := k.GetValidator(ctx, address)
if !found {
return
}
// delete the old validator record
store := ctx.KVStore(k.storeKey)
pool := k.getPool(store)
store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
store.Delete(GetValidatorsByPowerKey(validator, pool))
// delete from the current and power weighted validator groups if the validator
// is bonded - and add validator with zero power to the validator updates
if store.Get(GetValidatorsBondedKey(validator.PubKey)) == nil {
return
}
store.Delete(GetValidatorsBondedKey(validator.PubKey))
bz := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc))
store.Set(GetTendermintUpdatesKey(address), bz)
}
//_____________________________________________________________________
// load a delegator bond
func (k Keeper) GetDelegation(ctx sdk.Context,
delegatorAddr, validatorAddr sdk.Address) (bond Delegation, found bool) {
store := ctx.KVStore(k.storeKey)
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, validatorAddr, k.cdc))
if delegatorBytes == nil {
return bond, false
}
k.cdc.MustUnmarshalBinary(delegatorBytes, &bond)
return bond, true
}
// load all delegations used during genesis dump
func (k Keeper) getAllDelegations(ctx sdk.Context) (delegations []Delegation) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
i := 0
for ; ; i++ {
if !iterator.Valid() {
iterator.Close()
break
}
bondBytes := iterator.Value()
var delegation Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
delegations = append(delegations, delegation)
iterator.Next()
}
return delegations[:i] // trim
}
// load all bonds of a delegator
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest
bonds = make([]Delegation, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bondBytes := iterator.Value()
var bond Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &bond)
bonds[i] = bond
iterator.Next()
}
return bonds[:i] // trim
}
func (k Keeper) setDelegation(ctx sdk.Context, bond Delegation) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(bond)
store.Set(GetDelegationKey(bond.DelegatorAddr, bond.ValidatorAddr, k.cdc), b)
}
func (k Keeper) removeDelegation(ctx sdk.Context, bond Delegation) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegationKey(bond.DelegatorAddr, bond.ValidatorAddr, k.cdc))
}
//_______________________________________________________________________
// load/save the global staking params
func (k Keeper) GetParams(ctx sdk.Context) Params {
store := ctx.KVStore(k.storeKey)
return k.getParams(store)
}
func (k Keeper) getParams(store sdk.KVStore) (params Params) {
b := store.Get(ParamKey)
if b == nil {
panic("Stored params should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &params)
return
}
// Need a distinct function because setParams depends on an existing previous
// record of params to exist (to check if maxValidators has changed) - and we
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
// first params set it will panic.
func (k Keeper) setNewParams(ctx sdk.Context, params Params) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
// Public version of setNewParams
func (k Keeper) SetNewParams(ctx sdk.Context, params Params) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
func (k Keeper) setParams(ctx sdk.Context, params Params) {
store := ctx.KVStore(k.storeKey)
exParams := k.getParams(store)
// if max validator count changes, must recalculate validator set
if exParams.MaxValidators != params.MaxValidators {
k.updateBondedValidatorsFull(ctx, store)
}
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
//_______________________________________________________________________
// load/save the pool
func (k Keeper) GetPool(ctx sdk.Context) (pool Pool) {
store := ctx.KVStore(k.storeKey)
return k.getPool(store)
}
func (k Keeper) getPool(store sdk.KVStore) (pool Pool) {
b := store.Get(PoolKey)
if b == nil {
panic("Stored pool should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &pool)
return
}
func (k Keeper) setPool(ctx sdk.Context, pool Pool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(pool)
store.Set(PoolKey, b)
}
// Public version of setpool
func (k Keeper) SetPool(ctx sdk.Context, pool Pool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(pool)
store.Set(PoolKey, b)
}
//__________________________________________________________________________
// get the current in-block validator operation counter
func (k Keeper) getIntraTxCounter(ctx sdk.Context) int16 {
store := ctx.KVStore(k.storeKey)
b := store.Get(IntraTxCounterKey)
if b == nil {
return 0
}
var counter int16
k.cdc.MustUnmarshalBinary(b, &counter)
return counter
}
// set the current in-block validator operation counter
func (k Keeper) setIntraTxCounter(ctx sdk.Context, counter int16) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(counter)
store.Set(IntraTxCounterKey, bz)
}
//__________________________________________________________________________
// get the current validator on the cliff
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorCliffKey)
}
// get the current power of the validator on the cliff
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorPowerCliffKey)
}
// set the current validator and power of the validator on the cliff
func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Pool) {
store := ctx.KVStore(k.storeKey)
bz := GetValidatorsByPowerKey(validator, pool)
store.Set(ValidatorPowerCliffKey, bz)
store.Set(ValidatorCliffKey, validator.Owner)
}
// clear the current validator and power of the validator on the cliff
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
store.Delete(ValidatorPowerCliffKey)
store.Delete(ValidatorCliffKey)
}
//__________________________________________________________________________
// Implements ValidatorSet
var _ sdk.ValidatorSet = Keeper{}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var validator Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
address := iterator.Value()
validator, found := k.getValidator(store, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}
// get the sdk.validator for a particular address
func (k Keeper) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
val, found := k.GetValidator(ctx, addr)
if !found {
return nil
}
return val
}
// total power from the bond
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
pool := k.GetPool(ctx)
return pool.BondedShares
}
//__________________________________________________________________________
// Implements DelegationSet
var _ sdk.ValidatorSet = Keeper{}
// get the delegation for a particular set of delegator and validator addresses
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
if !ok {
return nil
}
return bond
}
// Returns self as it is both a validatorset and delegationset
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
return k
}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
store := ctx.KVStore(k.storeKey)
key := GetDelegationsKey(delAddr, k.cdc)
iterator := sdk.KVStorePrefixIterator(store, key)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var delegation Delegation
k.cdc.MustUnmarshalBinary(bz, &delegation)
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}
// slash a validator
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("attempted to slash a nonexistent validator with address %s", pubkey.Address()))
}
sharesToRemove := val.PoolShares.Amount.Mul(fraction)
pool := k.GetPool(ctx)
val, pool, burned := val.removePoolShares(pool, sharesToRemove)
k.setPool(ctx, pool) // update the pool
k.updateValidator(ctx, val) // update the validator, possibly kicking it out
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %v tokens", pubkey.Address(), fraction, sharesToRemove, burned))
return
}
// revoke a validator
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("validator with pubkey %s not found, cannot revoke", pubkey))
}
val.Revoked = true
k.updateValidator(ctx, val) // update the validator, now revoked
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
return
}
// unrevoke a validator
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("validator with pubkey %s not found, cannot unrevoke", pubkey))
}
val.Revoked = false
k.updateValidator(ctx, val) // update the validator, now unrevoked
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
return
}

View File

@ -0,0 +1,356 @@
package keeper
import (
"bytes"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// load a delegation
func (k Keeper) GetDelegation(ctx sdk.Context,
delegatorAddr, validatorAddr sdk.Address) (delegation types.Delegation, found bool) {
store := ctx.KVStore(k.storeKey)
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, validatorAddr, k.cdc))
if delegatorBytes == nil {
return delegation, false
}
k.cdc.MustUnmarshalBinary(delegatorBytes, &delegation)
return delegation, true
}
// load all delegations used during genesis dump
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
i := 0
for ; ; i++ {
if !iterator.Valid() {
break
}
bondBytes := iterator.Value()
var delegation types.Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
delegations = append(delegations, delegation)
iterator.Next()
}
iterator.Close()
return delegations
}
// load all delegations for a delegator
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address,
maxRetrieve int16) (delegations []types.Delegation) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest
delegations = make([]types.Delegation, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
break
}
bondBytes := iterator.Value()
var delegation types.Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
delegations[i] = delegation
iterator.Next()
}
iterator.Close()
return delegations[:i] // trim
}
// set the delegation
func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(delegation)
store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc), b)
}
// remove the delegation
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc))
}
//_____________________________________________________________________________________
// load a unbonding delegation
func (k Keeper) GetUnbondingDelegation(ctx sdk.Context,
DelegatorAddr, ValidatorAddr sdk.Address) (ubd types.UnbondingDelegation, found bool) {
store := ctx.KVStore(k.storeKey)
ubdKey := GetUBDKey(DelegatorAddr, ValidatorAddr, k.cdc)
bz := store.Get(ubdKey)
if bz == nil {
return ubd, false
}
k.cdc.MustUnmarshalBinary(bz, &ubd)
return ubd, true
}
// set the unbonding delegation and associated index
func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(ubd)
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
store.Set(ubdKey, bz)
store.Set(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc), ubdKey)
}
// remove the unbonding delegation object and associated index
func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
store := ctx.KVStore(k.storeKey)
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
store.Delete(ubdKey)
store.Delete(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc))
}
//_____________________________________________________________________________________
// load a redelegation
func (k Keeper) GetRedelegation(ctx sdk.Context,
DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr sdk.Address) (red types.Redelegation, found bool) {
store := ctx.KVStore(k.storeKey)
redKey := GetREDKey(DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr, k.cdc)
bz := store.Get(redKey)
if bz == nil {
return red, false
}
k.cdc.MustUnmarshalBinary(bz, &red)
return red, true
}
// has a redelegation
func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
DelegatorAddr, ValidatorDstAddr sdk.Address) bool {
store := ctx.KVStore(k.storeKey)
prefix := GetREDsByDelToValDstIndexKey(DelegatorAddr, ValidatorDstAddr, k.cdc)
iterator := sdk.KVStorePrefixIterator(store, prefix) //smallest to largest
found := false
if iterator.Valid() {
//record found
found = true
}
iterator.Close()
return found
}
// set a redelegation and associated index
func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(red)
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
store.Set(redKey, bz)
store.Set(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
}
// remove a redelegation object and associated index
func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
store := ctx.KVStore(k.storeKey)
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
store.Delete(redKey)
store.Delete(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
store.Delete(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
}
//_____________________________________________________________________________________
// Perform a delegation, set/update everything necessary within the store
func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.Address, bondAmt sdk.Coin,
validator types.Validator) (newShares sdk.Rat, err sdk.Error) {
// Get or create the delegator delegation
delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
if !found {
delegation = types.Delegation{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validator.Owner,
Shares: sdk.ZeroRat(),
}
}
// Account new shares, save
pool := k.GetPool(ctx)
_, _, err = k.coinKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt})
if err != nil {
return
}
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount.Int64())
delegation.Shares = delegation.Shares.Add(newShares)
// Update delegation height
delegation.Height = ctx.BlockHeight()
k.SetPool(ctx, pool)
k.SetDelegation(ctx, delegation)
k.UpdateValidator(ctx, validator)
return
}
// unbond the the delegation return
func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address,
shares sdk.Rat) (amount int64, err sdk.Error) {
// check if delegation has any shares in it unbond
delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr)
if !found {
err = types.ErrNoDelegatorForAddress(k.Codespace())
return
}
// retrieve the amount to remove
if delegation.Shares.LT(shares) {
err = types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())
return
}
// get validator
validator, found := k.GetValidator(ctx, validatorAddr)
if !found {
err = types.ErrNoValidatorFound(k.Codespace())
return
}
// subtract shares from delegator
delegation.Shares = delegation.Shares.Sub(shares)
// remove the delegation
if delegation.Shares.IsZero() {
// if the delegation is the owner of the validator then
// trigger a revoke validator
if bytes.Equal(delegation.DelegatorAddr, validator.Owner) && validator.Revoked == false {
validator.Revoked = true
}
k.RemoveDelegation(ctx, delegation)
} else {
// Update height
delegation.Height = ctx.BlockHeight()
k.SetDelegation(ctx, delegation)
}
// remove the coins from the validator
pool := k.GetPool(ctx)
validator, pool, amount = validator.RemoveDelShares(pool, shares)
k.SetPool(ctx, pool)
// update then remove validator if necessary
validator = k.UpdateValidator(ctx, validator)
if validator.DelegatorShares.IsZero() {
k.RemoveValidator(ctx, validator.Owner)
}
return amount, nil
}
//______________________________________________________________________________________________________
// complete unbonding an unbonding record
func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorAddr, sharesAmount)
if err != nil {
return err
}
// create the unbonding delegation
params := k.GetParams(ctx)
minTime := ctx.BlockHeader().Time + params.UnbondingTime
ubd := types.UnbondingDelegation{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
MinTime: minTime,
Balance: sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)},
}
k.SetUnbondingDelegation(ctx, ubd)
return nil
}
// complete unbonding an unbonding record
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address) sdk.Error {
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
if !found {
return types.ErrNoUnbondingDelegation(k.Codespace())
}
// ensure that enough time has passed
ctxTime := ctx.BlockHeader().Time
if ubd.MinTime > ctxTime {
return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime)
}
k.coinKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{ubd.Balance})
k.RemoveUnbondingDelegation(ctx, ubd)
return nil
}
// complete unbonding an unbonding record
func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr,
validatorDstAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
// check if this is a transitive redelegation
if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) {
return types.ErrTransitiveRedelegation(k.Codespace())
}
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorSrcAddr, sharesAmount)
if err != nil {
return err
}
params := k.GetParams(ctx)
returnCoin := sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)}
dstValidator, found := k.GetValidator(ctx, validatorDstAddr)
if !found {
return types.ErrBadRedelegationDst(k.Codespace())
}
sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator)
// create the unbonding delegation
minTime := ctx.BlockHeader().Time + params.UnbondingTime
red := types.Redelegation{
DelegatorAddr: delegatorAddr,
ValidatorSrcAddr: validatorSrcAddr,
ValidatorDstAddr: validatorDstAddr,
MinTime: minTime,
SharesDst: sharesCreated,
SharesSrc: sharesAmount,
}
k.SetRedelegation(ctx, red)
return nil
}
// complete unbonding an ongoing redelegation
func (k Keeper) CompleteRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address) sdk.Error {
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
if !found {
return types.ErrNoRedelegation(k.Codespace())
}
// ensure that enough time has passed
ctxTime := ctx.BlockHeader().Time
if red.MinTime > ctxTime {
return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime)
}
k.RemoveRedelegation(ctx, red)
return nil
}

View File

@ -0,0 +1,223 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// tests GetDelegation, GetDelegations, SetDelegation, RemoveDelegation, GetDelegations
func TestDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 10)
pool := keeper.GetPool(ctx)
//construct the validators
amts := []int64{9, 8, 7}
var validators [3]types.Validator
for i, amt := range amts {
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
}
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
// first add a validators[0] to delegate too
bond1to1 := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: sdk.NewRat(9),
}
// check the empty keeper first
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.False(t, found)
// set and retrieve a record
keeper.SetDelegation(ctx, bond1to1)
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.Equal(resBond))
// modify a records, save, and retrieve
bond1to1.Shares = sdk.NewRat(99)
keeper.SetDelegation(ctx, bond1to1)
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.Equal(resBond))
// add some more records
bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.SetDelegation(ctx, bond1to2)
keeper.SetDelegation(ctx, bond1to3)
keeper.SetDelegation(ctx, bond2to1)
keeper.SetDelegation(ctx, bond2to2)
keeper.SetDelegation(ctx, bond2to3)
// test all bond retrieve capabilities
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond1to1.Equal(resBonds[0]))
assert.True(t, bond1to2.Equal(resBonds[1]))
assert.True(t, bond1to3.Equal(resBonds[2]))
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
require.Equal(t, 3, len(resBonds))
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
require.Equal(t, 2, len(resBonds))
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond2to1.Equal(resBonds[0]))
assert.True(t, bond2to2.Equal(resBonds[1]))
assert.True(t, bond2to3.Equal(resBonds[2]))
allBonds := keeper.GetAllDelegations(ctx)
require.Equal(t, 6, len(allBonds))
assert.True(t, bond1to1.Equal(allBonds[0]))
assert.True(t, bond1to2.Equal(allBonds[1]))
assert.True(t, bond1to3.Equal(allBonds[2]))
assert.True(t, bond2to1.Equal(allBonds[3]))
assert.True(t, bond2to2.Equal(allBonds[4]))
assert.True(t, bond2to3.Equal(allBonds[5]))
// delete a record
keeper.RemoveDelegation(ctx, bond2to3)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
assert.False(t, found)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 2, len(resBonds))
assert.True(t, bond2to1.Equal(resBonds[0]))
assert.True(t, bond2to2.Equal(resBonds[1]))
// delete all the records from delegator 2
keeper.RemoveDelegation(ctx, bond2to1)
keeper.RemoveDelegation(ctx, bond2to2)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
assert.False(t, found)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
assert.False(t, found)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 0, len(resBonds))
}
// tests Get/Set/Remove UnbondingDelegation
func TestUnbondingDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
ubd := types.UnbondingDelegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
CreationHeight: 0,
MinTime: 0,
Balance: sdk.NewCoin("steak", 5),
}
// set and retrieve a record
keeper.SetUnbondingDelegation(ctx, ubd)
resBond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, ubd.Equal(resBond))
// modify a records, save, and retrieve
ubd.Balance = sdk.NewCoin("steak", 21)
keeper.SetUnbondingDelegation(ctx, ubd)
resBond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, ubd.Equal(resBond))
// delete a record
keeper.RemoveUnbondingDelegation(ctx, ubd)
_, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
assert.False(t, found)
}
func TestUnbondDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = 10
//create a validator and a delegator to that validator
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, 10)
require.Equal(t, int64(10), issuedShares.Evaluate())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
require.Equal(t, int64(10), pool.BondedTokens)
require.Equal(t, int64(10), validator.PoolShares.Bonded().Evaluate())
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
var err error
var amount int64
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6))
require.NoError(t, err)
assert.Equal(t, int64(6), amount) // shares to be added to an unbonding delegation / redelegation
delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
require.True(t, found)
validator, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
pool = keeper.GetPool(ctx)
assert.Equal(t, int64(4), delegation.Shares.Evaluate())
assert.Equal(t, int64(4), validator.PoolShares.Bonded().Evaluate())
assert.Equal(t, int64(6), pool.LooseTokens, "%v", pool)
assert.Equal(t, int64(4), pool.BondedTokens)
}
// tests Get/Set/Remove/Has UnbondingDelegation
func TestRedelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
rd := types.Redelegation{
DelegatorAddr: addrDels[0],
ValidatorSrcAddr: addrVals[0],
ValidatorDstAddr: addrVals[1],
CreationHeight: 0,
MinTime: 0,
SharesSrc: sdk.NewRat(5),
SharesDst: sdk.NewRat(5),
}
// test shouldn't have and redelegations
has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
assert.False(t, has)
// set and retrieve a record
keeper.SetRedelegation(ctx, rd)
resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
assert.True(t, found)
assert.True(t, rd.Equal(resBond))
// check if has the redelegation
has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
assert.True(t, has)
// modify a records, save, and retrieve
rd.SharesSrc = sdk.NewRat(21)
rd.SharesDst = sdk.NewRat(21)
keeper.SetRedelegation(ctx, rd)
resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
assert.True(t, found)
assert.True(t, rd.Equal(resBond))
// delete a record
keeper.RemoveRedelegation(ctx, rd)
_, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
assert.False(t, found)
}

View File

@ -1,35 +1,32 @@
package stake
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
const (
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
precision = 100000000000 // increased to this precision for accuracy with tests on tick_test.go
precision = 100000000000 // increased to this precision for accuracy
)
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
var hrsPerYrRat = sdk.NewRat(hrsPerYr)
// process provisions for an hour period
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
func (k Keeper) ProcessProvisions(ctx sdk.Context) types.Pool {
pool := k.GetPool(ctx)
pool.Inflation = k.nextInflation(ctx)
pool.Inflation = k.NextInflation(ctx)
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
// more bonded tokens are added proportionally to all validators the only term
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
provisions := pool.Inflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat).EvaluateInt()
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate()
// TODO add to the fees provisions
pool.LooseUnbondedTokens = pool.LooseUnbondedTokens.Add(provisions)
pool.LooseTokens += provisions
return pool
}
// get the next inflation rate for the hour
func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
func (k Keeper) NextInflation(ctx sdk.Context) (inflation sdk.Rat) {
params := k.GetParams(ctx)
pool := k.GetPool(ctx)
@ -40,7 +37,7 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
// 7% and 20%.
// (1 - bondedRatio/GoalBonded) * InflationRateChange
inflationRateChangePerYear := sdk.OneRat().Sub(pool.bondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
inflationRateChangePerYear := sdk.OneRat().Sub(pool.BondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat)
// increase the new annual inflation for this next cycle

View File

@ -1,65 +1,64 @@
package stake
package keeper
import (
"math/rand"
"strconv"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
var r = rand.New(rand.NewSource(6595))
func TestGetInflation(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
params := keeper.GetParams(ctx)
hrsPerYrRat := sdk.NewRat(hrsPerYr)
// Governing Mechanism:
// bondedRatio = BondedTokens / TotalSupply
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
zero := sdk.ZeroInt()
one := sdk.OneInt()
// BondedRatio = BondedTokens / TotalSupply
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
tests := []struct {
name string
setBondedTokens, setLooseTokens sdk.Int
setBondedTokens, setLooseTokens int64
setInflation, expectedChange sdk.Rat
}{
// with 0% bonded atom supply the inflation should increase by InflationRateChange
{"test 1", zero, zero, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
// 100% bonded, starting at 20% inflation and being reduced
// (1 - (1/0.67))*(0.13/8667)
{"test 2", one, zero, sdk.NewRat(20, 100),
{"test 2", 1, 0, sdk.NewRat(20, 100),
sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
// 50% bonded, starting at 10% inflation and being increased
{"test 3", one, one, sdk.NewRat(10, 100),
{"test 3", 1, 1, sdk.NewRat(10, 100),
sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
// test 7% minimum stop (testing with 100% bonded)
{"test 4", one, zero, sdk.NewRat(7, 100), sdk.ZeroRat()},
{"test 5", one, zero, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
{"test 4", 1, 0, sdk.NewRat(7, 100), sdk.ZeroRat()},
{"test 5", 1, 0, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
// test 20% maximum stop (testing with 0% bonded)
{"test 6", zero, zero, sdk.NewRat(20, 100), sdk.ZeroRat()},
{"test 7", zero, zero, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat()},
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
// perfect balance shouldn't change inflation
{"test 8", sdk.NewInt(67), sdk.NewInt(33), sdk.NewRat(15, 100), sdk.ZeroRat()},
{"test 8", 67, 33, sdk.NewRat(15, 100), sdk.ZeroRat()},
}
for _, tc := range tests {
pool.BondedTokens, pool.LooseUnbondedTokens = tc.setBondedTokens, tc.setLooseTokens
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
pool.Inflation = tc.setInflation
keeper.setPool(ctx, pool)
keeper.SetPool(ctx, pool)
inflation := keeper.nextInflation(ctx)
inflation := keeper.NextInflation(ctx)
diffInflation := inflation.Sub(tc.setInflation)
assert.True(t, diffInflation.Equal(tc.expectedChange),
@ -69,17 +68,18 @@ func TestGetInflation(t *testing.T) {
// Test that provisions are correctly added to the pool and validators each hour for 1 year
func TestProcessProvisions(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
var (
initialTotalTokens int64 = 550000000
initialBondedTokens int64 = 250000000
initialUnbondedTokens int64 = 300000000
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
bondedValidators uint16 = 2
initialTotalTokens int64 = 550000000
initialBondedTokens int64 = 250000000
initialUnbondedTokens int64 = 300000000
cumulativeExpProvs int64
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
bondedValidators uint16 = 2
)
pool.LooseTokens = initialTotalTokens
// create some validators some bonded, some unbonded
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
@ -89,7 +89,7 @@ func TestProcessProvisions(t *testing.T) {
for hr := 0; hr < 8766; hr++ {
pool := keeper.GetPool(ctx)
_, expProvisions, _ := updateProvisions(t, keeper, pool, ctx, hr)
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
cumulativeExpProvs = cumulativeExpProvs + expProvisions
}
//get the pool and do the final value checks from checkFinalPoolValues
@ -100,17 +100,18 @@ func TestProcessProvisions(t *testing.T) {
// Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate
// Cycles through the whole gambit of inflation possibilities, starting at 7% inflation, up to 20%, back down to 7% (it takes ~11.4 years)
func TestHourlyInflationRateOfChange(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
var (
initialTotalTokens int64 = 550000000
initialBondedTokens int64 = 150000000
initialUnbondedTokens int64 = 400000000
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
bondedValidators uint16 = 1
initialTotalTokens int64 = 550000000
initialBondedTokens int64 = 150000000
initialUnbondedTokens int64 = 400000000
cumulativeExpProvs int64
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
bondedValidators uint16 = 1
)
pool.LooseTokens = initialTotalTokens
// create some validators some bonded, some unbonded
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
@ -121,7 +122,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
pool := keeper.GetPool(ctx)
previousInflation := pool.Inflation
updatedInflation, expProvisions, pool := updateProvisions(t, keeper, pool, ctx, hr)
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
cumulativeExpProvs = cumulativeExpProvs + expProvisions
msg := strconv.Itoa(hr)
checkInflation(t, pool, previousInflation, updatedInflation, msg)
}
@ -133,7 +134,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
//Test that a large unbonding will significantly lower the bonded ratio
func TestLargeUnbond(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
var (
@ -147,32 +148,33 @@ func TestLargeUnbond(t *testing.T) {
validatorTokens = []int64{300000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000}
bondedValidators uint16 = 7
)
pool.LooseTokens = initialTotalTokens
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
pool = keeper.GetPool(ctx)
validator, found := keeper.GetValidator(ctx, addrs[0])
validator, found := keeper.GetValidator(ctx, Addrs[0])
assert.True(t, found)
// initialBondedRatio that we can use to compare to the new values after the unbond
initialBondedRatio := pool.bondedRatio()
initialBondedRatio := pool.BondedRatio()
// validator[0] will be unbonded, bringing us from 75% bonded ratio to ~50% (unbonding 300,000,000)
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
keeper.setPool(ctx, pool)
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
keeper.SetPool(ctx, pool)
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
bondedShares = bondedShares.Sub(bondSharesVal0)
val0UnbondedTokens = pool.unbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.unbondedShareExRate()))
val0UnbondedTokens = pool.UnbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.UnbondedShareExRate()))
// unbonded shares should increase
assert.True(t, unbondedShares.GT(sdk.NewRat(300000000, 1)))
// Ensure that new bonded ratio is less than old bonded ratio , because before they were increasing (i.e. 50% < 75)
assert.True(t, (pool.bondedRatio().LT(initialBondedRatio)))
assert.True(t, (pool.BondedRatio().LT(initialBondedRatio)))
// Final check that the pool equals initial values + provisions and adjustments we recorded
pool = keeper.GetPool(ctx)
@ -181,7 +183,7 @@ func TestLargeUnbond(t *testing.T) {
//Test that a large bonding will significantly increase the bonded ratio
func TestLargeBond(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
var (
@ -193,24 +195,25 @@ func TestLargeBond(t *testing.T) {
validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000}
bondedValidators uint16 = 1
)
pool.LooseTokens = initialTotalTokens
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
pool = keeper.GetPool(ctx)
validator, found := keeper.GetValidator(ctx, addrs[9])
validator, found := keeper.GetValidator(ctx, Addrs[9])
assert.True(t, found)
// initialBondedRatio that we can use to compare to the new values after the unbond
initialBondedRatio := pool.bondedRatio()
initialBondedRatio := pool.BondedRatio()
params := DefaultParams()
params := types.DefaultParams()
params.MaxValidators = bondedValidators + 1 //must do this to allow for an extra validator to bond
keeper.setParams(ctx, params)
keeper.SetParams(ctx, params)
// validator[9] will be bonded, bringing us from 25% to ~50% (bonding 400,000,000 tokens)
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
keeper.setPool(ctx, pool)
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
keeper.SetPool(ctx, pool)
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
@ -219,7 +222,7 @@ func TestLargeBond(t *testing.T) {
// unbonded shares should decrease
assert.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1)))
// Ensure that new bonded ratio is greater than old bonded ratio (i.e. 50% > 25%)
assert.True(t, (pool.bondedRatio().GT(initialBondedRatio)))
assert.True(t, (pool.BondedRatio().GT(initialBondedRatio)))
// Final check that the pool equals initial values + provisions and adjustments we recorded
pool = keeper.GetPool(ctx)
@ -228,20 +231,19 @@ func TestLargeBond(t *testing.T) {
// Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators
func TestInflationWithRandomOperations(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
params := DefaultParams()
keeper.setParams(ctx, params)
ctx, _, keeper := CreateTestInput(t, false, 0)
params := types.DefaultParams()
keeper.SetParams(ctx, params)
numValidators := 20
// start off by randomly setting up 20 validators
pool, validators := randomSetup(r, numValidators)
pool, validators := types.RandomSetup(r, numValidators)
require.Equal(t, numValidators, len(validators))
for i := 0; i < len(validators); i++ {
keeper.setValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
}
keeper.setPool(ctx, pool)
keeper.SetPool(ctx, pool)
// Used to rotate validators so each random operation is applied to a different validator
validatorCounter := 0
@ -250,30 +252,30 @@ func TestInflationWithRandomOperations(t *testing.T) {
for i := 0; i < numValidators; i++ {
pool := keeper.GetPool(ctx)
// Get inflation before randomOperation, for comparison later
// Get inflation before RandomOperation, for comparison later
previousInflation := pool.Inflation
// Perform the random operation, and record how validators are modified
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, pool, validators[validatorCounter])
validatorsMod := make([]Validator, len(validators))
poolMod, validatorMod, tokens, msg := types.RandomOperation(r)(r, pool, validators[validatorCounter])
validatorsMod := make([]types.Validator, len(validators))
copy(validatorsMod[:], validators[:])
require.Equal(t, numValidators, len(validators), "i %v", validatorCounter)
require.Equal(t, numValidators, len(validatorsMod), "i %v", validatorCounter)
validatorsMod[validatorCounter] = validatorMod
assertInvariants(t, msg,
types.AssertInvariants(t, msg,
pool, validators,
poolMod, validatorsMod, tokens)
// set pool and validators after the random operation
pool = poolMod
keeper.setPool(ctx, pool)
keeper.SetPool(ctx, pool)
validators = validatorsMod
// Must set inflation here manually, as opposed to most other tests in this suite, where we call keeper.processProvisions(), which updates pool.Inflation
updatedInflation := keeper.nextInflation(ctx)
updatedInflation := keeper.NextInflation(ctx)
pool.Inflation = updatedInflation
keeper.setPool(ctx, pool)
keeper.SetPool(ctx, pool)
// Ensure inflation changes as expected when random operations are applied.
checkInflation(t, pool, previousInflation, updatedInflation, msg)
@ -285,41 +287,43 @@ func TestInflationWithRandomOperations(t *testing.T) {
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens int64, cumulativeExpProvs sdk.Int) {
calculatedTotalTokens := cumulativeExpProvs.AddRaw(initialTotalTokens)
assert.Equal(t, calculatedTotalTokens.Int64(), pool.TokenSupply().Int64())
func checkFinalPoolValues(t *testing.T, pool types.Pool, initialTotalTokens, cumulativeExpProvs int64) {
calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs
assert.Equal(t, calculatedTotalTokens, pool.TokenSupply())
}
// Processes provisions are added to the pool correctly every hour
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
func updateProvisions(t *testing.T, keeper Keeper, pool Pool, ctx sdk.Context, hr int) (sdk.Rat, sdk.Int, Pool) {
expInflation := keeper.nextInflation(ctx)
expProvisions := (expInflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat)).EvaluateInt()
func updateProvisions(t *testing.T, keeper Keeper, pool types.Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, types.Pool) {
expInflation := keeper.NextInflation(ctx)
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate()
startTotalSupply := pool.TokenSupply()
pool = keeper.processProvisions(ctx)
keeper.setPool(ctx, pool)
pool = keeper.ProcessProvisions(ctx)
keeper.SetPool(ctx, pool)
//check provisions were added to pool
require.Equal(t, startTotalSupply.Add(expProvisions).Int64(), pool.TokenSupply().Int64())
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
return expInflation, expProvisions, pool
}
// Deterministic setup of validators and pool
// Allows you to decide how many validators to setup
// Allows you to pick which validators are bonded by adjusting the MaxValidators of params
func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64, maxValidators uint16) ([]Validator, Keeper, Pool) {
params := DefaultParams()
func setupTestValidators(pool types.Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64,
maxValidators uint16) ([]types.Validator, Keeper, types.Pool) {
params := types.DefaultParams()
params.MaxValidators = maxValidators
keeper.setParams(ctx, params)
keeper.SetParams(ctx, params)
numValidators := len(validatorTokens)
validators := make([]Validator, numValidators)
validators := make([]types.Validator, numValidators)
for i := 0; i < numValidators; i++ {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i], pool, _ = validators[i].addTokensFromDel(pool, sdk.NewInt(validatorTokens[i]))
keeper.setPool(ctx, pool)
validators[i] = keeper.updateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, validatorTokens[i])
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
pool = keeper.GetPool(ctx)
}
@ -327,28 +331,28 @@ func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTok
}
// Checks that the deterministic validator setup you wanted matches the values in the pool
func checkValidatorSetup(t *testing.T, pool Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
assert.Equal(t, initialTotalTokens, pool.TokenSupply().Int64())
assert.Equal(t, initialBondedTokens, pool.BondedTokens.Int64())
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens.Int64())
func checkValidatorSetup(t *testing.T, pool types.Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
assert.Equal(t, initialTotalTokens, pool.TokenSupply(), "%v", pool)
assert.Equal(t, initialBondedTokens, pool.BondedTokens, "%v", pool)
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens, "%v", pool)
// test initial bonded ratio
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.bondedRatio())
assert.True(t, pool.BondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.BondedRatio())
// test the value of validator shares
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
assert.True(t, pool.BondedShareExRate().Equal(sdk.OneRat()), "%v", pool.BondedShareExRate())
}
// Checks that The inflation will correctly increase or decrease after an update to the pool
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
func checkInflation(t *testing.T, pool types.Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
inflationChange := updatedInflation.Sub(previousInflation)
switch {
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
assert.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
if previousInflation.Equal(sdk.NewRat(20, 100)) {
assert.Equal(t, true, inflationChange.IsZero(), msg)
@ -358,11 +362,11 @@ func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation
}
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
if previousInflation.Equal(sdk.NewRat(7, 100)) {
assert.Equal(t, true, inflationChange.IsZero(), msg)

122
x/stake/keeper/keeper.go Normal file
View File

@ -0,0 +1,122 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// keeper of the stake store
type Keeper struct {
storeKey sdk.StoreKey
cdc *wire.Codec
coinKeeper bank.Keeper
// codespace
codespace sdk.CodespaceType
}
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
coinKeeper: ck,
codespace: codespace,
}
return keeper
}
//_________________________________________________________________________
// return the codespace
func (k Keeper) Codespace() sdk.CodespaceType {
return k.codespace
}
//_________________________________________________________________________
// some generic reads/writes that don't need their own files
// load/save the global staking params
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
store := ctx.KVStore(k.storeKey)
b := store.Get(ParamKey)
if b == nil {
panic("Stored params should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &params)
return
}
// Need a distinct function because setParams depends on an existing previous
// record of params to exist (to check if maxValidators has changed) - and we
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
// first params set it will panic.
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
// set the params
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
exParams := k.GetParams(ctx)
// if max validator count changes, must recalculate validator set
if exParams.MaxValidators != params.MaxValidators {
k.UpdateBondedValidatorsFull(ctx)
}
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
//_______________________________________________________________________
// load/save the pool
func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
panic("Stored pool should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &pool)
return
}
// set the pool
func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(pool)
store.Set(PoolKey, b)
}
//__________________________________________________________________________
// get the current in-block validator operation counter
func (k Keeper) InitIntraTxCounter(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
b := store.Get(IntraTxCounterKey)
if b == nil {
k.SetIntraTxCounter(ctx, 0)
}
}
// get the current in-block validator operation counter
func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 {
store := ctx.KVStore(k.storeKey)
b := store.Get(IntraTxCounterKey)
var counter int16
k.cdc.MustUnmarshalBinary(b, &counter)
return counter
}
// set the current in-block validator operation counter
func (k Keeper) SetIntraTxCounter(ctx sdk.Context, counter int16) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(counter)
store.Set(IntraTxCounterKey, bz)
}

View File

@ -0,0 +1,39 @@
package keeper
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
func TestParams(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
expParams := types.DefaultParams()
//check that the empty keeper loads the default
resParams := keeper.GetParams(ctx)
assert.True(t, expParams.Equal(resParams))
//modify a params, save, and retrieve
expParams.MaxValidators = 777
keeper.SetParams(ctx, expParams)
resParams = keeper.GetParams(ctx)
assert.True(t, expParams.Equal(resParams))
}
func TestPool(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
expPool := types.InitialPool()
//check that the empty keeper loads the default
resPool := keeper.GetPool(ctx)
assert.True(t, expPool.Equal(resPool))
//modify a params, save, and retrieve
expPool.BondedTokens = 777
keeper.SetPool(ctx, expPool)
resPool = keeper.GetPool(ctx)
assert.True(t, expPool.Equal(resPool))
}

197
x/stake/keeper/key.go Normal file
View File

@ -0,0 +1,197 @@
package keeper
import (
"encoding/binary"
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// TODO remove some of these prefixes once have working multistore
//nolint
var (
// Keys for store prefixes
ParamKey = []byte{0x00} // key for parameters relating to staking
PoolKey = []byte{0x01} // key for the staking pools
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
IntraTxCounterKey = []byte{0x09} // key for intra-block tx index
DelegationKey = []byte{0x0A} // key for a delegation
UnbondingDelegationKey = []byte{0x0B} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x0C} // prefix for each key for an unbonding-delegation, by validator owner
RedelegationKey = []byte{0x0D} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by validator owner
RedelegationByValDstIndexKey = []byte{0x0F} // prefix for each key for an redelegation, by validator owner
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
// get the key for the validator with address
func GetValidatorKey(ownerAddr sdk.Address) []byte {
return append(ValidatorsKey, ownerAddr.Bytes()...)
}
// get the key for the validator with pubkey
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
}
// get the key for the current validator group, ordered like tendermint
func GetValidatorsBondedIndexKey(ownerAddr sdk.Address) []byte {
return append(ValidatorsBondedIndexKey, ownerAddr.Bytes()...)
}
// get the power which is the key for the validator used in the power-store
func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte {
// NOTE the address doesn't need to be stored because counter bytes must always be different
return GetValidatorPowerRank(validator, pool)
}
// get the power of a validator
func GetValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
power := validator.EquivalentBondedShares(pool)
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
// TODO ensure that the key will be a readable string.. probably should add seperators and have
revokedBytes := make([]byte, 1)
if validator.Revoked {
revokedBytes[0] = byte(0x01)
} else {
revokedBytes[0] = byte(0x00)
}
// TODO ensure that the key will be a readable string.. probably should add seperators and have
// heightBytes and counterBytes represent strings like powerBytes does
heightBytes := make([]byte, binary.MaxVarintLen64)
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
counterBytes := make([]byte, 2)
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
return append(ValidatorsByPowerIndexKey,
append(revokedBytes,
append(powerBytes,
append(heightBytes, counterBytes...)...)...)...)
}
// get the key for the accumulated update validators
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
}
//________________________________________________________________________________
// get the key for delegator bond with validator
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
}
// get the prefix for a delegator for all validators
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&delegatorAddr)
return append(DelegationKey, res...)
}
//________________________________________________________________________________
// get the key for an unbonding delegation
func GetUBDKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetUBDsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
}
// get the index-key for an unbonding delegation, stored by validator-index
func GetUBDByValIndexKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetUBDsByValIndexKey(validatorAddr, cdc), delegatorAddr.Bytes()...)
}
//______________
// get the prefix for all unbonding delegations from a delegator
func GetUBDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&delegatorAddr)
return append(UnbondingDelegationKey, res...)
}
// get the prefix keyspace for the indexs of unbonding delegations for a validator
func GetUBDsByValIndexKey(validatorAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&validatorAddr)
return append(UnbondingDelegationByValIndexKey, res...)
}
//________________________________________________________________________________
// get the key for a redelegation
func GetREDKey(delegatorAddr, validatorSrcAddr,
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
return append(
GetREDsKey(delegatorAddr, cdc),
append(
validatorSrcAddr.Bytes(),
validatorDstAddr.Bytes()...)...,
)
}
// get the index-key for a redelegation, stored by source-validator-index
func GetREDByValSrcIndexKey(delegatorAddr, validatorSrcAddr,
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
return append(
GetREDsFromValSrcIndexKey(validatorSrcAddr, cdc),
append(
delegatorAddr.Bytes(),
validatorDstAddr.Bytes()...)...,
)
}
// get the index-key for a redelegation, stored by destination-validator-index
func GetREDByValDstIndexKey(delegatorAddr, validatorSrcAddr,
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
return append(
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
append(
delegatorAddr.Bytes(),
validatorSrcAddr.Bytes()...)...,
)
}
//______________
// get the prefix keyspace for redelegations from a delegator
func GetREDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&delegatorAddr)
return append(RedelegationKey, res...)
}
// get the prefix keyspace for all redelegations redelegating away from a source validator
func GetREDsFromValSrcIndexKey(validatorSrcAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&validatorSrcAddr)
return append(RedelegationByValSrcIndexKey, res...)
}
// get the prefix keyspace for all redelegations redelegating towards a destination validator
func GetREDsToValDstIndexKey(validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
res := cdc.MustMarshalBinary(&validatorDstAddr)
return append(RedelegationByValDstIndexKey, res...)
}
// get the prefix keyspace for all redelegations redelegating towards a destination validator
// from a particular delegator
func GetREDsByDelToValDstIndexKey(delegatorAddr sdk.Address,
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
return append(
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
delegatorAddr.Bytes()...)
}

104
x/stake/keeper/sdk_types.go Normal file
View File

@ -0,0 +1,104 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// Implements ValidatorSet
var _ sdk.ValidatorSet = Keeper{}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var validator types.Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}
// get the sdk.validator for a particular address
func (k Keeper) Validator(ctx sdk.Context, address sdk.Address) sdk.Validator {
val, found := k.GetValidator(ctx, address)
if !found {
return nil
}
return val
}
// total power from the bond
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
pool := k.GetPool(ctx)
return pool.BondedShares
}
//__________________________________________________________________________
// Implements DelegationSet
var _ sdk.DelegationSet = Keeper{}
// Returns self as it is both a validatorset and delegationset
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
return k
}
// get the delegation for a particular set of delegator and validator addresses
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
if !ok {
return nil
}
return bond
}
// iterate through the active validator set and perform the provided function
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
store := ctx.KVStore(k.storeKey)
key := GetDelegationsKey(delAddr, k.cdc)
iterator := sdk.KVStorePrefixIterator(store, key)
i := int64(0)
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var delegation types.Delegation
k.cdc.MustUnmarshalBinary(bz, &delegation)
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
if stop {
break
}
i++
}
iterator.Close()
}

59
x/stake/keeper/slash.go Normal file
View File

@ -0,0 +1,59 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
// NOTE the current slash functionality doesn't take into consideration unbonding/rebonding records
// or the time of breach. This will be updated in slashing v2
// slash a validator
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Attempted to slash a nonexistent validator with address %s", pubkey.Address()))
}
sharesToRemove := validator.PoolShares.Amount.Mul(fraction)
pool := k.GetPool(ctx)
validator, pool, burned := validator.RemovePoolShares(pool, sharesToRemove)
k.SetPool(ctx, pool) // update the pool
k.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %d tokens", pubkey.Address(), fraction, sharesToRemove, burned))
return
}
// revoke a validator
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot revoke", pubkey))
}
validator.Revoked = true
k.UpdateValidator(ctx, validator) // update the validator, now revoked
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
return
}
// unrevoke a validator
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot unrevoke", pubkey))
}
validator.Revoked = false
k.UpdateValidator(ctx, validator) // update the validator, now unrevoked
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
return
}

View File

@ -1,4 +1,4 @@
package stake
package keeper
import (
"bytes"
@ -18,35 +18,52 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// dummy addresses used for testing
var (
addrs = createTestAddrs(100)
pks = createTestPubKeys(100)
Addrs = createTestAddrs(100)
PKs = createTestPubKeys(100)
emptyAddr sdk.Address
emptyPubkey crypto.PubKey
addrDels = []sdk.Address{
Addrs[0],
Addrs[1],
}
addrVals = []sdk.Address{
Addrs[2],
Addrs[3],
Addrs[4],
Addrs[5],
Addrs[6],
}
)
//_______________________________________________________________________________________
// intended to be used with require/assert: require.True(ValEq(...))
func ValEq(t *testing.T, exp, got Validator) (*testing.T, bool, string, Validator, Validator) {
return t, exp.equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
}
//_______________________________________________________________________________________
func makeTestCodec() *wire.Codec {
// create a codec used only for testing
func MakeTestCodec() *wire.Codec {
var cdc = wire.NewCodec()
// Register Msgs
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil)
cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil)
cdc.RegisterConcrete(MsgCreateValidator{}, "test/stake/CreateValidator", nil)
cdc.RegisterConcrete(MsgEditValidator{}, "test/stake/EditValidator", nil)
cdc.RegisterConcrete(MsgUnbond{}, "test/stake/Unbond", nil)
cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil)
cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil)
cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil)
cdc.RegisterConcrete(types.MsgCompleteUnbonding{}, "test/stake/CompleteUnbonding", nil)
cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/stake/BeginRedelegate", nil)
cdc.RegisterConcrete(types.MsgCompleteRedelegate{}, "test/stake/CompleteRedelegate", nil)
// Register AppAccount
cdc.RegisterInterface((*auth.Account)(nil), nil)
@ -56,8 +73,9 @@ func makeTestCodec() *wire.Codec {
return cdc
}
func paramsNoInflation() Params {
return Params{
// default params without inflation
func ParamsNoInflation() types.Params {
return types.Params{
InflationRateChange: sdk.ZeroRat(),
InflationMax: sdk.ZeroRat(),
InflationMin: sdk.ZeroRat(),
@ -68,7 +86,7 @@ func paramsNoInflation() Params {
}
// hogpodge of all sorts of input required for testing
func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Context, auth.AccountMapper, Keeper) {
func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) {
keyStake := sdk.NewKVStoreKey("stake")
keyAcc := sdk.NewKVStoreKey("acc")
@ -81,28 +99,32 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Conte
require.Nil(t, err)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
cdc := makeTestCodec()
cdc := MakeTestCodec()
accountMapper := auth.NewAccountMapper(
cdc, // amino codec
keyAcc, // target store
&auth.BaseAccount{}, // prototype
)
ck := bank.NewKeeper(accountMapper)
keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace)
keeper.setPool(ctx, InitialPool())
keeper.setNewParams(ctx, DefaultParams())
keeper := NewKeeper(cdc, keyStake, ck, types.DefaultCodespace)
keeper.SetPool(ctx, types.InitialPool())
keeper.SetNewParams(ctx, types.DefaultParams())
keeper.InitIntraTxCounter(ctx)
// fill all the addresses with some coins
for _, addr := range addrs {
// fill all the addresses with some coins, set the loose pool tokens simultaneously
for _, addr := range Addrs {
pool := keeper.GetPool(ctx)
ck.AddCoins(ctx, addr, sdk.Coins{
{keeper.GetParams(ctx).BondDenom, initCoins},
{keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
})
pool.LooseTokens += initCoins
keeper.SetPool(ctx, pool)
}
return ctx, accountMapper, keeper
}
func newPubKey(pk string) (res crypto.PubKey) {
func NewPubKey(pk string) (res crypto.PubKey) {
pkBytes, err := hex.DecodeString(pk)
if err != nil {
panic(err)
@ -114,7 +136,7 @@ func newPubKey(pk string) (res crypto.PubKey) {
}
// for incode address generation
func testAddr(addr string, bech string) sdk.Address {
func TestAddr(addr string, bech string) sdk.Address {
res, err := sdk.GetAccAddressHex(addr)
if err != nil {
@ -151,7 +173,7 @@ func createTestAddrs(numAddrs int) []sdk.Address {
buffer.WriteString(numString) //adding on final two digits to make addresses unique
res, _ := sdk.GetAccAddressHex(buffer.String())
bech, _ := sdk.Bech32ifyAcc(res)
addresses = append(addresses, testAddr(buffer.String(), bech))
addresses = append(addresses, TestAddr(buffer.String(), bech))
buffer.Reset()
}
return addresses
@ -166,8 +188,16 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
numString := strconv.Itoa(i)
buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string
buffer.WriteString(numString) //adding on final two digits to make pubkeys unique
publicKeys = append(publicKeys, newPubKey(buffer.String()))
publicKeys = append(publicKeys, NewPubKey(buffer.String()))
buffer.Reset()
}
return publicKeys
}
//_____________________________________________________________________________________
// does a certain by-power index record exist
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
store := ctx.KVStore(keeper.storeKey)
return store.Get(power) != nil
}

537
x/stake/keeper/validator.go Normal file
View File

@ -0,0 +1,537 @@
package keeper
import (
"bytes"
"fmt"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// get a single validator
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetValidatorKey(addr))
if b == nil {
return validator, false
}
k.cdc.MustUnmarshalBinary(b, &validator)
return validator, true
}
// get a single validator by pubkey
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
if addr == nil {
return validator, false
}
return k.GetValidator(ctx, addr)
}
// set the main record holding validator details
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
// set main store
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bz)
}
// validator index
func (k Keeper) SetValidatorByPubKeyIndex(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
// set pointer by pubkey
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
}
// validator index
func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.Owner)
}
// validator index
func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
}
// used in testing
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
store := ctx.KVStore(k.storeKey)
return store.Get(power) != nil
}
// Get the set of all validators with no limits, used during genesis dump
func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
i := 0
for ; ; i++ {
if !iterator.Valid() {
break
}
bz := iterator.Value()
var validator types.Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
validators = append(validators, validator)
iterator.Next()
}
iterator.Close()
return validators
}
// Get the set of all validators, retrieve a maxRetrieve number of records
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
validators = make([]types.Validator, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
break
}
bz := iterator.Value()
var validator types.Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
validators[i] = validator
iterator.Next()
}
iterator.Close()
return validators[:i] // trim
}
//___________________________________________________________________________
// get the group of the bonded validators
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
validators = make([]types.Validator, maxValidators)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
i := 0
for ; iterator.Valid(); iterator.Next() {
// sanity check
if i > int(maxValidators-1) {
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
}
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
validators[i] = validator
i++
}
iterator.Close()
return validators[:i] // trim
}
// get the group of bonded validators sorted by power-rank
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
validators := make([]types.Validator, maxValidators)
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
i := 0
for {
if !iterator.Valid() || i > int(maxValidators-1) {
break
}
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
if validator.Status() == sdk.Bonded {
validators[i] = validator
i++
}
iterator.Next()
}
iterator.Close()
return validators[:i] // trim
}
//_________________________________________________________________________
// Accumulated updates to the active/bonded validator set for tendermint
// get the most recently updated validators
func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
for ; iterator.Valid(); iterator.Next() {
valBytes := iterator.Value()
var val abci.Validator
k.cdc.MustUnmarshalBinary(valBytes, &val)
updates = append(updates, val)
}
iterator.Close()
return
}
// remove all validator update entries after applied to Tendermint
func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//___________________________________________________________________________
// perfom all the nessisary steps for when a validator changes its power
// updates all validator stores as well as tendermint update store
// may kick out validators if new validator is entering the bonded validator group
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
ownerAddr := validator.Owner
// always update the main list ordered by owner address before exiting
defer func() {
bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(ownerAddr), bz)
}()
// retrieve the old validator record
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
validator = k.unbondValidator(ctx, validator)
// need to also clear the cliff validator spot because the revoke has
// opened up a new spot which will be filled when
// updateValidatorsBonded is called
k.clearCliffValidator(ctx)
}
powerIncreasing := false
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
powerIncreasing = true
}
// if already a validator, copy the old block height and counter, else set them
if oldFound && oldValidator.Status() == sdk.Bonded {
validator.BondHeight = oldValidator.BondHeight
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
} else {
validator.BondHeight = ctx.BlockHeight()
counter := k.GetIntraTxCounter(ctx)
validator.BondIntraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
}
// update the list ordered by voting power
if oldFound {
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
}
valPower := GetValidatorsByPowerIndexKey(validator, pool)
store.Set(valPower, validator.Owner)
// efficiency case:
// if already bonded and power increasing only need to update tendermint
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
return validator
}
// efficiency case:
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
cliffPower := k.getCliffValidatorPower(ctx)
if cliffPower != nil &&
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
return validator
}
// update the validator set for this validator
updatedVal := k.UpdateBondedValidators(ctx, validator)
if updatedVal.Owner != nil { // updates to validator occurred to be updated
validator = updatedVal
}
return validator
}
// Update the validator group and kick out any old validators. In addition this
// function adds (or doesn't add) a validator which has updated its bonded
// tokens to the validator group. -> this validator is specified through the
// updatedValidatorAddr term.
//
// The correct subset is retrieved by iterating through an index of the
// validators sorted by power, stored using the ValidatorsByPowerIndexKey.
// Simultaneously the current validator records are updated in store with the
// ValidatorsBondedIndexKey. This store is used to determine if a validator is a
// validator without needing to iterate over the subspace as we do in
// GetValidators.
//
// Optionally also return the validator from a retrieve address if the validator has been bonded
func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
newValidator types.Validator) (updatedVal types.Validator) {
store := ctx.KVStore(k.storeKey)
kickCliffValidator := false
oldCliffValidatorAddr := k.getCliffValidator(ctx)
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
bondedValidatorsCount := 0
var validator types.Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
// TODO benchmark if we should read the current power and not write if it's the same
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
}
break
}
// either retrieve the original validator from the store, or under the
// situation that this is the "new validator" just use the validator
// provided because it has not yet been updated in the main validator
// store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator
} else {
var found bool
validator, found = k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
}
// if not previously a validator (and unrevoked),
// kick the cliff validator / bond this new validator
if validator.Status() != sdk.Bonded && !validator.Revoked {
kickCliffValidator = true
validator = k.bondValidator(ctx, validator)
if bytes.Equal(ownerAddr, newValidator.Owner) {
updatedVal = validator
}
}
if validator.Revoked && validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
} else {
bondedValidatorsCount++
}
iterator.Next()
}
iterator.Close()
// perform the actual kicks
if oldCliffValidatorAddr != nil && kickCliffValidator {
validator, found := k.GetValidator(ctx, oldCliffValidatorAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
}
k.unbondValidator(ctx, validator)
}
return
}
// full update of the bonded validator set, many can be added/kicked
func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// clear the current validators store, add to the ToKickOut temp store
toKickOut := make(map[string]byte)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
for ; iterator.Valid(); iterator.Next() {
ownerAddr := iterator.Value()
toKickOut[string(ownerAddr)] = 0 // set anything
}
iterator.Close()
// add the actual validator power sorted store
maxValidators := k.GetParams(ctx).MaxValidators
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
bondedValidatorsCount := 0
var validator types.Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
}
break
}
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
ownerAddr := iterator.Value()
var found bool
validator, found = k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
_, found = toKickOut[string(ownerAddr)]
if found {
delete(toKickOut, string(ownerAddr))
} else {
// if it wasn't in the toKickOut group it means
// this wasn't a previously a validator, therefor
// update the validator to enter the validator group
validator = k.bondValidator(ctx, validator)
}
if validator.Revoked && validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
} else {
bondedValidatorsCount++
}
iterator.Next()
}
iterator.Close()
// perform the actual kicks
for key := range toKickOut {
ownerAddr := []byte(key)
validator, found := k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
k.unbondValidator(ctx, validator)
}
return
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
// sanity check
if validator.Status() == sdk.Unbonded {
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
k.SetPool(ctx, pool)
// save the now unbonded validator record
bzVal := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bzVal)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
// also remove from the Bonded types.Validators Store
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
return validator
}
// perform all the store operations for when a validator status becomes bonded
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
// sanity check
if validator.Status() == sdk.Bonded {
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
k.SetPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
bzVal := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bzVal)
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
return validator
}
// remove the validator record and associated indexes
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.Address) {
// first retrieve the old validator record
validator, found := k.GetValidator(ctx, address)
if !found {
return
}
// delete the old validator record
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
// delete from the current and power weighted validator groups if the validator
// is bonded - and add validator with zero power to the validator updates
if store.Get(GetValidatorsBondedIndexKey(validator.Owner)) == nil {
return
}
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
store.Set(GetTendermintUpdatesKey(address), bz)
}
//__________________________________________________________________________
// get the current validator on the cliff
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorCliffIndexKey)
}
// get the current power of the validator on the cliff
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorPowerCliffKey)
}
// set the current validator and power of the validator on the cliff
func (k Keeper) setCliffValidator(ctx sdk.Context, validator types.Validator, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
bz := GetValidatorsByPowerIndexKey(validator, pool)
store.Set(ValidatorPowerCliffKey, bz)
store.Set(ValidatorCliffIndexKey, validator.Owner)
}
// clear the current validator and power of the validator on the cliff
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
store.Delete(ValidatorPowerCliffKey)
store.Delete(ValidatorCliffIndexKey)
}

View File

@ -0,0 +1,674 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSetValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 10)
pool := keeper.GetPool(ctx)
// test how the validator is set from a purely unbonbed pool
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, 10)
require.Equal(t, sdk.Unbonded, validator.Status())
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
// after the save the validator should be bonded
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status())
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
// Check each store for being saved
resVal, found := keeper.GetValidator(ctx, addrVals[0])
assert.True(ValEq(t, validator, resVal))
resVals := keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
resVals = keeper.GetValidatorsByPower(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
assert.Equal(t, validator.ABCIValidator(keeper.cdc), updates[0])
}
func TestUpdateValidatorByPowerIndex(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
// create a random pool
pool.LooseTokens = 10000
pool.BondedTokens = 1234
pool.BondedShares = sdk.NewRat(124)
pool.UnbondingTokens = 13934
pool.UnbondingShares = sdk.NewRat(145)
pool.UnbondedTokens = 154
pool.UnbondedShares = sdk.NewRat(1333)
keeper.SetPool(ctx, pool)
// add a validator
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, delSharesCreated := validator.AddTokensFromDel(pool, 100)
require.Equal(t, sdk.Unbonded, validator.Status())
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
pool = keeper.GetPool(ctx)
power := GetValidatorsByPowerIndexKey(validator, pool)
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
// burn half the delegator shares
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
assert.Equal(t, int64(50), burned)
keeper.SetPool(ctx, pool) // update the pool
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, addrVals[0])
power = GetValidatorsByPowerIndexKey(validator, pool)
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
}
// This function tests UpdateValidator, GetValidator, GetValidatorsBonded, RemoveValidator
func TestValidatorBasics(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
pool := keeper.GetPool(ctx)
//construct the validators
var validators [3]types.Validator
amts := []int64{9, 8, 7}
for i, amt := range amts {
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
validators[i].PoolShares = types.NewUnbondedShares(sdk.ZeroRat())
validators[i].AddTokensFromDel(pool, amt)
}
// check the empty keeper first
_, found := keeper.GetValidator(ctx, addrVals[0])
assert.False(t, found)
resVals := keeper.GetValidatorsBonded(ctx)
assert.Zero(t, len(resVals))
// set and retrieve a record
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0]))
// modify a records, save, and retrieve
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(10))
validators[0].DelegatorShares = sdk.NewRat(10)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resVal, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0]))
// add other validators
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
resVal, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
assert.True(ValEq(t, validators[1], resVal))
resVal, found = keeper.GetValidator(ctx, addrVals[2])
require.True(t, found)
assert.True(ValEq(t, validators[2], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 3, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here
assert.True(ValEq(t, validators[1], resVals[1]))
assert.True(ValEq(t, validators[2], resVals[2]))
// remove a record
keeper.RemoveValidator(ctx, validators[1].Owner)
_, found = keeper.GetValidator(ctx, addrVals[1])
assert.False(t, found)
}
// test how the validators are sorted, tests GetValidatorsByPower
func GetValidatorSortingUnmixed(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
// initialize some validators into the state
amts := []int64{0, 100, 1, 400, 200}
n := len(amts)
var validators [5]types.Validator
for i, amt := range amts {
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i].PoolShares = types.NewBondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
keeper.UpdateValidator(ctx, validators[i])
}
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
// test a basic increase in voting power
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(500))
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
// test a decrease in voting power
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
// test equal voting power, different age
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(200))
ctx = ctx.WithBlockHeight(10)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
// no change in voting power - no change in sort
ctx = ctx.WithBlockHeight(20)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
// change in voting power of both validators, both still in v-set, no age change
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(300))
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
ctx = ctx.WithBlockHeight(30)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n, "%v", resValidators)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
}
func GetValidatorSortingMixed(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
// now 2 max resValidators
params := keeper.GetParams(ctx)
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 1, 400, 200}
n := len(amts)
var validators [5]types.Validator
for i, amt := range amts {
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[0]))
validators[1].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[1]))
validators[2].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[2]))
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(amts[3]))
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(amts[4]))
for i := range amts {
keeper.UpdateValidator(ctx, validators[i])
}
val0, found := keeper.GetValidator(ctx, Addrs[0])
require.True(t, found)
val1, found := keeper.GetValidator(ctx, Addrs[1])
require.True(t, found)
val2, found := keeper.GetValidator(ctx, Addrs[2])
require.True(t, found)
val3, found := keeper.GetValidator(ctx, Addrs[3])
require.True(t, found)
val4, found := keeper.GetValidator(ctx, Addrs[4])
require.True(t, found)
assert.Equal(t, sdk.Unbonded, val0.Status())
assert.Equal(t, sdk.Unbonded, val1.Status())
assert.Equal(t, sdk.Unbonded, val2.Status())
assert.Equal(t, sdk.Bonded, val3.Status())
assert.Equal(t, sdk.Bonded, val4.Status())
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
}
// TODO separate out into multiple tests
func TestGetValidatorsEdgeCases(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
var found bool
// now 2 max resValidators
params := keeper.GetParams(ctx)
nMax := uint16(2)
params.MaxValidators = nMax
keeper.SetParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 400, 400}
var validators [4]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
}
for i := range amts {
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
require.True(t, found)
}
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[2], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 500)
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
// A validator which leaves the gotValidator set due to a decrease in voting power,
// then increases to the original voting power, does not get its spot back in the
// case of a tie.
// validator 3 enters bonded validator set
ctx = ctx.WithBlockHeight(40)
validators[3], found = keeper.GetValidator(ctx, validators[3].Owner)
require.True(t, found)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 1)
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
// validator 3 kicked out temporarily
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewRat(201))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
// validator 4 does not get spot back
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 200)
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
validator, exists := keeper.GetValidator(ctx, validators[3].Owner)
require.Equal(t, exists, true)
require.Equal(t, int64(40), validator.BondHeight)
}
func TestValidatorBondHeight(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
// now 2 max resValidators
params := keeper.GetParams(ctx)
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// initialize some validators into the state
pool := keeper.GetPool(ctx)
var validators [3]types.Validator
validators[0] = types.NewValidator(Addrs[0], PKs[0], types.Description{})
validators[1] = types.NewValidator(Addrs[1], PKs[1], types.Description{})
validators[2] = types.NewValidator(Addrs[2], PKs[2], types.Description{})
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 200)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 100)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 100)
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
////////////////////////////////////////
// If two validators both increase to the same voting power in the same block,
// the one with the first transaction should become bonded
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[1], resValidators[1]))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 50)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 50)
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
validators[1] = keeper.UpdateValidator(ctx, validators[1])
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
func TestFullValidatorSetPowerChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
max := 2
params.MaxValidators = uint16(2)
keeper.SetParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 400, 400, 200}
var validators [5]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validators[i])
}
for i := range amts {
var found bool
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
require.True(t, found)
}
assert.Equal(t, sdk.Unbonded, validators[0].Status())
assert.Equal(t, sdk.Unbonded, validators[1].Status())
assert.Equal(t, sdk.Bonded, validators[2].Status())
assert.Equal(t, sdk.Bonded, validators[3].Status())
assert.Equal(t, sdk.Unbonded, validators[4].Status())
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
assert.True(ValEq(t, validators[3], resValidators[1]))
// test a swap in voting power
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 600)
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
// clear the tracked changes to the gotValidator set
func TestClearTendermintUpdates(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 400, 200}
validators := make([]types.Validator, len(amts))
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validators[i])
}
updates := keeper.GetTendermintUpdates(ctx)
assert.Equal(t, len(amts), len(updates))
keeper.ClearTendermintUpdates(ctx)
updates = keeper.GetTendermintUpdates(ctx)
assert.Equal(t, 0, len(updates))
}
func TestGetTendermintUpdatesAllNone(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
// test from nothing to something
// tendermintUpdate set: {} -> {c1, c3}
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
assert.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
// test from something to nothing
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
keeper.RemoveValidator(ctx, validators[0].Owner)
keeper.RemoveValidator(ctx, validators[1].Owner)
updates = keeper.GetTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
assert.Equal(t, int64(0), updates[0].Power)
assert.Equal(t, int64(0), updates[1].Power)
}
func TestGetTendermintUpdatesIdentical(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
}
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(600))
validators[0] = keeper.UpdateValidator(ctx, validators[0])
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
}
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 190)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 80)
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
require.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
}
func TestGetTendermintUpdatesInserted(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20, 5, 15, 25}
var validators [5]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[0])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
updates = keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[3].ABCIValidator(keeper.cdc), updates[0])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
validators[4] = keeper.UpdateValidator(ctx, validators[4])
updates = keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[4].ABCIValidator(keeper.cdc), updates[0])
}
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := types.DefaultParams()
params.MaxValidators = 2
keeper.SetParams(ctx, params)
amts := []int64{10, 20, 5}
var validators [5]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 0, len(updates))
// test validator change its power and become a gotValidator (pushing out an existing)
// tendermintUpdate set: {} -> {c0, c4}
keeper.ClearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
pool := keeper.GetPool(ctx)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 10)
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates = keeper.GetTendermintUpdates(ctx)
require.Equal(t, 2, len(updates), "%v", updates)
require.Equal(t, validators[0].ABCIValidatorZero(keeper.cdc), updates[0])
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[1])
}

View File

@ -1,89 +0,0 @@
package stake
import (
"encoding/binary"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
crypto "github.com/tendermint/go-crypto"
)
// TODO remove some of these prefixes once have working multistore
//nolint
var (
// Keys for store prefixes
ParamKey = []byte{0x00} // key for parameters relating to staking
PoolKey = []byte{0x01} // key for the staking pools
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator by pubkey
ValidatorsBondedKey = []byte{0x04} // prefix for each key to bonded/actively validating validators
ValidatorsByPowerKey = []byte{0x05} // prefix for each key to a validator sorted by power
ValidatorCliffKey = []byte{0x06} // key for block-local tx index
ValidatorPowerCliffKey = []byte{0x07} // key for block-local tx index
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
DelegationKey = []byte{0x09} // prefix for each key to a delegator's bond
IntraTxCounterKey = []byte{0x10} // key for block-local tx index
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
// get the key for the validator with address
func GetValidatorKey(ownerAddr sdk.Address) []byte {
return append(ValidatorsKey, ownerAddr.Bytes()...)
}
// get the key for the validator with pubkey
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
}
// get the key for the current validator group, ordered like tendermint
func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
addr := pk.Address()
return append(ValidatorsBondedKey, addr.Bytes()...)
}
// get the key for the validator used in the power-store
func GetValidatorsByPowerKey(validator Validator, pool Pool) []byte {
power := validator.EquivalentBondedShares(pool)
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
// TODO ensure that the key will be a readable string.. probably should add seperators and have
revokedBytes := make([]byte, 1)
if validator.Revoked {
revokedBytes[0] = byte(0x01)
} else {
revokedBytes[0] = byte(0x00)
}
// heightBytes and counterBytes represent strings like powerBytes does
heightBytes := make([]byte, binary.MaxVarintLen64)
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
counterBytes := make([]byte, 2)
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
return append(ValidatorsByPowerKey,
append(revokedBytes,
append(powerBytes,
append(heightBytes,
append(counterBytes, validator.Owner.Bytes()...)...)...)...)...) // TODO don't technically need to store owner
}
// get the key for the accumulated update validators
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
}
// get the key for delegator bond with validator
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
}
// get the prefix for a delegator for all validators
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res, err := cdc.MarshalBinary(&delegatorAddr)
if err != nil {
panic(err)
}
return append(DelegationKey, res...)
}

View File

@ -1,792 +0,0 @@
package stake
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
addrDels = []sdk.Address{
addrs[0],
addrs[1],
}
addrVals = []sdk.Address{
addrs[2],
addrs[3],
addrs[4],
addrs[5],
addrs[6],
}
)
func TestUpdateValidatorByPowerIndex(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
pool := keeper.GetPool(ctx)
// create a random pool
pool.BondedTokens = sdk.NewInt(1234)
pool.BondedShares = sdk.NewRat(124)
pool.UnbondingTokens = sdk.NewInt(13934)
pool.UnbondingShares = sdk.NewRat(145)
pool.UnbondedTokens = sdk.NewInt(154)
pool.UnbondedShares = sdk.NewRat(1333)
keeper.setPool(ctx, pool)
// add a validator
validator := NewValidator(addrVals[0], pks[0], Description{})
validator, pool, delSharesCreated := validator.addTokensFromDel(pool, sdk.NewInt(100))
require.Equal(t, sdk.Unbonded, validator.Status())
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
keeper.setPool(ctx, pool)
keeper.updateValidator(ctx, validator)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
pool = keeper.GetPool(ctx)
power := GetValidatorsByPowerKey(validator, pool)
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
// burn half the delegator shares
validator, pool, burned := validator.removeDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
assert.Equal(t, int64(50), burned.Int64())
keeper.setPool(ctx, pool) // update the pool
keeper.updateValidator(ctx, validator) // update the validator, possibly kicking it out
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, addrVals[0])
power = GetValidatorsByPowerKey(validator, pool)
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
}
func TestSetValidator(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
pool := keeper.GetPool(ctx)
// test how the validator is set from a purely unbonbed pool
validator := NewValidator(addrVals[0], pks[0], Description{})
validator, pool, _ = validator.addTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, sdk.Unbonded, validator.Status())
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
keeper.setPool(ctx, pool)
keeper.updateValidator(ctx, validator)
// after the save the validator should be bonded
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status())
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
// Check each store for being saved
resVal, found := keeper.GetValidator(ctx, addrVals[0])
assert.True(ValEq(t, validator, resVal))
resVals := keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
resVals = keeper.GetValidatorsByPower(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
assert.Equal(t, validator.abciValidator(keeper.cdc), updates[0])
}
// This function tests updateValidator, GetValidator, GetValidatorsBonded, removeValidator
func TestValidatorBasics(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
pool := keeper.GetPool(ctx)
//construct the validators
var validators [3]Validator
amts := []int64{9, 8, 7}
for i, amt := range amts {
validators[i] = NewValidator(addrVals[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.ZeroRat())
validators[i].addTokensFromDel(pool, sdk.NewInt(amt))
}
// check the empty keeper first
_, found := keeper.GetValidator(ctx, addrVals[0])
assert.False(t, found)
resVals := keeper.GetValidatorsBonded(ctx)
assert.Zero(t, len(resVals))
// set and retrieve a record
validators[0] = keeper.updateValidator(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0]))
// modify a records, save, and retrieve
validators[0].PoolShares = NewBondedShares(sdk.NewRat(10))
validators[0].DelegatorShares = sdk.NewRat(10)
validators[0] = keeper.updateValidator(ctx, validators[0])
resVal, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0]))
// add other validators
validators[1] = keeper.updateValidator(ctx, validators[1])
validators[2] = keeper.updateValidator(ctx, validators[2])
resVal, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
assert.True(ValEq(t, validators[1], resVal))
resVal, found = keeper.GetValidator(ctx, addrVals[2])
require.True(t, found)
assert.True(ValEq(t, validators[2], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 3, len(resVals))
assert.True(ValEq(t, validators[0], resVals[2])) // order doesn't matter here
assert.True(ValEq(t, validators[1], resVals[1]))
assert.True(ValEq(t, validators[2], resVals[0]))
// remove a record
keeper.removeValidator(ctx, validators[1].Owner)
_, found = keeper.GetValidator(ctx, addrVals[1])
assert.False(t, found)
}
// test how the validators are sorted, tests GetValidatorsByPower
func GetValidatorSortingUnmixed(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
// initialize some validators into the state
amts := []int64{0, 100, 1, 400, 200}
n := len(amts)
var validators [5]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewBondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
keeper.updateValidator(ctx, validators[i])
}
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
// test a basic increase in voting power
validators[3].PoolShares = NewBondedShares(sdk.NewRat(500))
keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
// test a decrease in voting power
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
// test equal voting power, different age
validators[3].PoolShares = NewBondedShares(sdk.NewRat(200))
ctx = ctx.WithBlockHeight(10)
keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
// no change in voting power - no change in sort
ctx = ctx.WithBlockHeight(20)
keeper.updateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
// change in voting power of both validators, both still in v-set, no age change
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
validators[4].PoolShares = NewBondedShares(sdk.NewRat(300))
keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
ctx = ctx.WithBlockHeight(30)
keeper.updateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n, "%v", resValidators)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
}
func GetValidatorSortingMixed(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
// now 2 max resValidators
params := keeper.GetParams(ctx)
params.MaxValidators = 2
keeper.setParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 1, 400, 200}
n := len(amts)
var validators [5]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(amts[0]))
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(amts[1]))
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(amts[2]))
validators[3].PoolShares = NewBondedShares(sdk.NewRat(amts[3]))
validators[4].PoolShares = NewBondedShares(sdk.NewRat(amts[4]))
for i := range amts {
keeper.updateValidator(ctx, validators[i])
}
val0, found := keeper.GetValidator(ctx, addrs[0])
require.True(t, found)
val1, found := keeper.GetValidator(ctx, addrs[1])
require.True(t, found)
val2, found := keeper.GetValidator(ctx, addrs[2])
require.True(t, found)
val3, found := keeper.GetValidator(ctx, addrs[3])
require.True(t, found)
val4, found := keeper.GetValidator(ctx, addrs[4])
require.True(t, found)
assert.Equal(t, sdk.Unbonded, val0.Status())
assert.Equal(t, sdk.Unbonded, val1.Status())
assert.Equal(t, sdk.Unbonded, val2.Status())
assert.Equal(t, sdk.Bonded, val3.Status())
assert.Equal(t, sdk.Bonded, val4.Status())
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
}
// TODO seperate out into multiple tests
func TestGetValidatorsEdgeCases(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
var found bool
// now 2 max resValidators
params := keeper.GetParams(ctx)
nMax := uint16(2)
params.MaxValidators = nMax
keeper.setParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 400, 400}
var validators [4]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
validators[i] = keeper.updateValidator(ctx, validators[i])
}
for i := range amts {
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
require.True(t, found)
}
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[2], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(500))
validators[0] = keeper.updateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
// A validator which leaves the gotValidator set due to a decrease in voting power,
// then increases to the original voting power, does not get its spot back in the
// case of a tie.
// validator 3 enters bonded validator set
ctx = ctx.WithBlockHeight(40)
validators[3], found = keeper.GetValidator(ctx, validators[3].Owner)
require.True(t, found)
validators[3].PoolShares = NewUnbondedShares(sdk.NewRat(401))
validators[3] = keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
// validator 3 kicked out temporarily
validators[3].PoolShares = NewBondedShares(sdk.NewRat(200))
validators[3] = keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
// validator 4 does not get spot back
validators[3].PoolShares = NewBondedShares(sdk.NewRat(400))
validators[3] = keeper.updateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
validator, exists := keeper.GetValidator(ctx, validators[3].Owner)
require.Equal(t, exists, true)
require.Equal(t, int64(40), validator.BondHeight)
}
func TestValidatorBondHeight(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
// now 2 max resValidators
params := keeper.GetParams(ctx)
params.MaxValidators = 2
keeper.setParams(ctx, params)
// initialize some validators into the state
var validators [3]Validator
validators[0] = NewValidator(addrs[0], pks[0], Description{})
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(200))
validators[0].DelegatorShares = sdk.NewRat(200)
validators[1] = NewValidator(addrs[1], pks[1], Description{})
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(100))
validators[1].DelegatorShares = sdk.NewRat(100)
validators[2] = NewValidator(addrs[2], pks[2], Description{})
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(100))
validators[2].DelegatorShares = sdk.NewRat(100)
validators[0] = keeper.updateValidator(ctx, validators[0])
////////////////////////////////////////
// If two validators both increase to the same voting power in the same block,
// the one with the first transaction should become bonded
validators[1] = keeper.updateValidator(ctx, validators[1])
validators[2] = keeper.updateValidator(ctx, validators[2])
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[1], resValidators[1]))
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(150))
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(150))
validators[2] = keeper.updateValidator(ctx, validators[2])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
validators[1] = keeper.updateValidator(ctx, validators[1])
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
func TestFullValidatorSetPowerChange(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
params := keeper.GetParams(ctx)
max := 2
params.MaxValidators = uint16(2)
keeper.setParams(ctx, params)
// initialize some validators into the state
amts := []int64{0, 100, 400, 400, 200}
var validators [5]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
keeper.updateValidator(ctx, validators[i])
}
for i := range amts {
var found bool
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
require.True(t, found)
}
assert.Equal(t, sdk.Unbonded, validators[0].Status())
assert.Equal(t, sdk.Unbonded, validators[1].Status())
assert.Equal(t, sdk.Bonded, validators[2].Status())
assert.Equal(t, sdk.Bonded, validators[3].Status())
assert.Equal(t, sdk.Unbonded, validators[4].Status())
resValidators := keeper.GetValidatorsByPower(ctx)
require.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
assert.True(ValEq(t, validators[3], resValidators[1]))
// test a swap in voting power
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(600))
validators[0] = keeper.updateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
require.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
// clear the tracked changes to the gotValidator set
func TestClearTendermintUpdates(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{100, 400, 200}
validators := make([]Validator, len(amts))
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
keeper.updateValidator(ctx, validators[i])
}
updates := keeper.getTendermintUpdates(ctx)
assert.Equal(t, len(amts), len(updates))
keeper.clearTendermintUpdates(ctx)
updates = keeper.getTendermintUpdates(ctx)
assert.Equal(t, 0, len(updates))
}
func TestGetTendermintUpdatesAllNone(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{10, 20}
var validators [2]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
// test from nothing to something
// tendermintUpdate set: {} -> {c1, c3}
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
assert.Equal(t, validators[1].abciValidator(keeper.cdc), updates[1])
// test from something to nothing
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
keeper.removeValidator(ctx, validators[0].Owner)
keeper.removeValidator(ctx, validators[1].Owner)
updates = keeper.getTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
assert.Equal(t, int64(0), updates[0].Power)
assert.Equal(t, int64(0), updates[1].Power)
}
func TestGetTendermintUpdatesIdentical(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{10, 20}
var validators [2]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
}
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{10, 20}
var validators [2]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
validators[0].PoolShares = NewBondedShares(sdk.NewRat(600))
validators[0] = keeper.updateValidator(ctx, validators[0])
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
assert.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
}
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{10, 20}
var validators [2]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
validators[0].PoolShares = NewBondedShares(sdk.NewRat(200))
validators[1].PoolShares = NewBondedShares(sdk.NewRat(100))
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
require.Equal(t, validators[1].abciValidator(keeper.cdc), updates[1])
}
func TestGetTendermintUpdatesInserted(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
amts := []int64{10, 20, 5, 15, 25}
var validators [5]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
validators[2] = keeper.updateValidator(ctx, validators[2])
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[2].abciValidator(keeper.cdc), updates[0])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
keeper.clearTendermintUpdates(ctx)
validators[3] = keeper.updateValidator(ctx, validators[3])
updates = keeper.getTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[3].abciValidator(keeper.cdc), updates[0])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
keeper.clearTendermintUpdates(ctx)
validators[4] = keeper.updateValidator(ctx, validators[4])
updates = keeper.getTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[4].abciValidator(keeper.cdc), updates[0])
}
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
params := DefaultParams()
params.MaxValidators = 2
keeper.setParams(ctx, params)
amts := []int64{10, 20, 5}
var validators [5]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrs[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
validators[0] = keeper.updateValidator(ctx, validators[0])
validators[1] = keeper.updateValidator(ctx, validators[1])
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
keeper.updateValidator(ctx, validators[2])
updates := keeper.getTendermintUpdates(ctx)
require.Equal(t, 0, len(updates))
// test validator change its power and become a gotValidator (pushing out an existing)
// tendermintUpdate set: {} -> {c0, c4}
keeper.clearTendermintUpdates(ctx)
assert.Equal(t, 0, len(keeper.getTendermintUpdates(ctx)))
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(15))
validators[2] = keeper.updateValidator(ctx, validators[2])
updates = keeper.getTendermintUpdates(ctx)
require.Equal(t, 2, len(updates), "%v", updates)
require.Equal(t, validators[0].abciValidatorZero(keeper.cdc), updates[0])
require.Equal(t, validators[2].abciValidator(keeper.cdc), updates[1])
}
// tests GetDelegation, GetDelegations, SetDelegation, removeDelegation, GetBonds
func TestBond(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
//construct the validators
amts := []int64{9, 8, 7}
var validators [3]Validator
for i, amt := range amts {
validators[i] = NewValidator(addrVals[i], pks[i], Description{})
validators[i].PoolShares = NewUnbondedShares(sdk.NewRat(amt))
validators[i].DelegatorShares = sdk.NewRat(amt)
}
// first add a validators[0] to delegate too
validators[0] = keeper.updateValidator(ctx, validators[0])
bond1to1 := Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: sdk.NewRat(9),
}
// check the empty keeper first
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.False(t, found)
// set and retrieve a record
keeper.setDelegation(ctx, bond1to1)
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// modify a records, save, and retrieve
bond1to1.Shares = sdk.NewRat(99)
keeper.setDelegation(ctx, bond1to1)
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// add some more records
validators[1] = keeper.updateValidator(ctx, validators[1])
validators[2] = keeper.updateValidator(ctx, validators[2])
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.setDelegation(ctx, bond1to2)
keeper.setDelegation(ctx, bond1to3)
keeper.setDelegation(ctx, bond2to1)
keeper.setDelegation(ctx, bond2to2)
keeper.setDelegation(ctx, bond2to3)
// test all bond retrieve capabilities
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond1to1.equal(resBonds[0]))
assert.True(t, bond1to2.equal(resBonds[1]))
assert.True(t, bond1to3.equal(resBonds[2]))
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
require.Equal(t, 3, len(resBonds))
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
require.Equal(t, 2, len(resBonds))
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond2to1.equal(resBonds[0]))
assert.True(t, bond2to2.equal(resBonds[1]))
assert.True(t, bond2to3.equal(resBonds[2]))
allBonds := keeper.getAllDelegations(ctx)
require.Equal(t, 6, len(allBonds))
assert.True(t, bond1to1.equal(allBonds[0]))
assert.True(t, bond1to2.equal(allBonds[1]))
assert.True(t, bond1to3.equal(allBonds[2]))
assert.True(t, bond2to1.equal(allBonds[3]))
assert.True(t, bond2to2.equal(allBonds[4]))
assert.True(t, bond2to3.equal(allBonds[5]))
// delete a record
keeper.removeDelegation(ctx, bond2to3)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
assert.False(t, found)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 2, len(resBonds))
assert.True(t, bond2to1.equal(resBonds[0]))
assert.True(t, bond2to2.equal(resBonds[1]))
// delete all the records from delegator 2
keeper.removeDelegation(ctx, bond2to1)
keeper.removeDelegation(ctx, bond2to2)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
assert.False(t, found)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
assert.False(t, found)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 0, len(resBonds))
}
func TestParams(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
expParams := DefaultParams()
//check that the empty keeper loads the default
resParams := keeper.GetParams(ctx)
assert.True(t, expParams.equal(resParams))
//modify a params, save, and retrieve
expParams.MaxValidators = 777
keeper.setParams(ctx, expParams)
resParams = keeper.GetParams(ctx)
assert.True(t, expParams.equal(resParams))
}
func TestPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
expPool := InitialPool()
//check that the empty keeper loads the default
resPool := keeper.GetPool(ctx)
assert.True(t, expPool.equal(resPool))
//modify a params, save, and retrieve
expPool.BondedTokens = sdk.NewInt(777)
keeper.setPool(ctx, expPool)
resPool = keeper.GetPool(ctx)
assert.True(t, expPool.equal(resPool))
}

View File

@ -1,243 +0,0 @@
package stake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
// name to idetify transaction types
const MsgType = "stake"
// XXX remove: think it makes more sense belonging with the Params so we can
// initialize at genesis - to allow for the same tests we should should make
// the ValidateBasic() function a return from an initializable function
// ValidateBasic(bondDenom string) function
const StakingToken = "steak"
//Verify interface at compile time
var _, _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}, &MsgUnbond{}
//______________________________________________________________________
// MsgCreateValidator - struct for unbonding transactions
type MsgCreateValidator struct {
Description
ValidatorAddr sdk.Address `json:"address"`
PubKey crypto.PubKey `json:"pubkey"`
Bond sdk.Coin `json:"bond"`
}
func NewMsgCreateValidator(validatorAddr sdk.Address, pubkey crypto.PubKey,
bond sdk.Coin, description Description) MsgCreateValidator {
return MsgCreateValidator{
Description: description,
ValidatorAddr: validatorAddr,
PubKey: pubkey,
Bond: bond,
}
}
//nolint
func (msg MsgCreateValidator) Type() string { return MsgType }
func (msg MsgCreateValidator) GetSigners() []sdk.Address {
return []sdk.Address{msg.ValidatorAddr}
}
// get the bytes for the message signer to sign on
func (msg MsgCreateValidator) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(struct {
Description
ValidatorAddr string `json:"address"`
PubKey string `json:"pubkey"`
Bond sdk.Coin `json:"bond"`
}{
Description: msg.Description,
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
PubKey: sdk.MustBech32ifyValPub(msg.PubKey),
})
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil {
return ErrValidatorEmpty(DefaultCodespace)
}
if msg.Bond.Denom != StakingToken {
return ErrBadBondingDenom(DefaultCodespace)
}
if msg.Bond.Amount.Sign() != 1 {
return ErrBadBondingAmount(DefaultCodespace)
}
empty := Description{}
if msg.Description == empty {
return newError(DefaultCodespace, CodeInvalidInput, "description must be included")
}
return nil
}
//______________________________________________________________________
// MsgEditValidator - struct for editing a validator
type MsgEditValidator struct {
Description
ValidatorAddr sdk.Address `json:"address"`
}
func NewMsgEditValidator(validatorAddr sdk.Address, description Description) MsgEditValidator {
return MsgEditValidator{
Description: description,
ValidatorAddr: validatorAddr,
}
}
//nolint
func (msg MsgEditValidator) Type() string { return MsgType }
func (msg MsgEditValidator) GetSigners() []sdk.Address {
return []sdk.Address{msg.ValidatorAddr}
}
// get the bytes for the message signer to sign on
func (msg MsgEditValidator) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(struct {
Description
ValidatorAddr string `json:"address"`
}{
Description: msg.Description,
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
})
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgEditValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil {
return ErrValidatorEmpty(DefaultCodespace)
}
empty := Description{}
if msg.Description == empty {
return newError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
}
return nil
}
//______________________________________________________________________
// MsgDelegate - struct for bonding transactions
type MsgDelegate struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"validator_addr"`
Bond sdk.Coin `json:"bond"`
}
func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Bond: bond,
}
}
//nolint
func (msg MsgDelegate) Type() string { return MsgType }
func (msg MsgDelegate) GetSigners() []sdk.Address {
return []sdk.Address{msg.DelegatorAddr}
}
// get the bytes for the message signer to sign on
func (msg MsgDelegate) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(struct {
DelegatorAddr string `json:"delegator_addr"`
ValidatorAddr string `json:"validator_addr"`
Bond sdk.Coin `json:"bond"`
}{
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
Bond: msg.Bond,
})
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgDelegate) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrBadDelegatorAddr(DefaultCodespace)
}
if msg.ValidatorAddr == nil {
return ErrBadValidatorAddr(DefaultCodespace)
}
if msg.Bond.Denom != StakingToken {
return ErrBadBondingDenom(DefaultCodespace)
}
if msg.Bond.Amount.Sign() != 1 {
return ErrBadBondingAmount(DefaultCodespace)
}
return nil
}
//______________________________________________________________________
// MsgUnbond - struct for unbonding transactions
type MsgUnbond struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"validator_addr"`
Shares string `json:"shares"`
}
func NewMsgUnbond(delegatorAddr, validatorAddr sdk.Address, shares string) MsgUnbond {
return MsgUnbond{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Shares: shares,
}
}
//nolint
func (msg MsgUnbond) Type() string { return MsgType }
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
// get the bytes for the message signer to sign on
func (msg MsgUnbond) GetSignBytes() []byte {
b, err := msgCdc.MarshalJSON(struct {
DelegatorAddr string `json:"delegator_addr"`
ValidatorAddr string `json:"validator_addr"`
Shares string `json:"shares"`
}{
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
Shares: msg.Shares,
})
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgUnbond) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrBadDelegatorAddr(DefaultCodespace)
}
if msg.ValidatorAddr == nil {
return ErrBadValidatorAddr(DefaultCodespace)
}
if msg.Shares != "MAX" {
rat, err := sdk.NewRatFromDecimal(msg.Shares)
if err != nil {
return ErrBadShares(DefaultCodespace)
}
if rat.IsZero() || rat.LT(sdk.ZeroRat()) {
return ErrBadShares(DefaultCodespace)
}
}
return nil
}

View File

@ -1,156 +0,0 @@
package stake
import (
"testing"
"github.com/stretchr/testify/assert"
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
var (
coinPos = sdk.NewCoin("steak", 1000)
coinZero = sdk.NewCoin("steak", 0)
coinNeg = sdk.NewCoin("steak", -10000)
coinPosNotAtoms = sdk.NewCoin("foo", 10000)
coinZeroNotAtoms = sdk.NewCoin("foo", 0)
coinNegNotAtoms = sdk.NewCoin("foo", -10000)
)
// test ValidateBasic for MsgCreateValidator
func TestMsgCreateValidator(t *testing.T) {
tests := []struct {
name, moniker, identity, website, details string
validatorAddr sdk.Address
pubkey crypto.PubKey
bond sdk.Coin
expectPass bool
}{
{"basic good", "a", "b", "c", "d", addrs[0], pks[0], coinPos, true},
{"partial description", "", "", "c", "", addrs[0], pks[0], coinPos, true},
{"empty description", "", "", "", "", addrs[0], pks[0], coinPos, false},
{"empty address", "a", "b", "c", "d", emptyAddr, pks[0], coinPos, false},
{"empty pubkey", "a", "b", "c", "d", addrs[0], emptyPubkey, coinPos, true},
{"empty bond", "a", "b", "c", "d", addrs[0], pks[0], coinZero, false},
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
{"wrong staking token", "a", "b", "c", "d", addrs[0], pks[0], coinPosNotAtoms, false},
}
for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}
// test ValidateBasic for MsgEditValidator
func TestMsgEditValidator(t *testing.T) {
tests := []struct {
name, moniker, identity, website, details string
validatorAddr sdk.Address
expectPass bool
}{
{"basic good", "a", "b", "c", "d", addrs[0], true},
{"partial description", "", "", "c", "", addrs[0], true},
{"empty description", "", "", "", "", addrs[0], false},
{"empty address", "a", "b", "c", "d", emptyAddr, false},
}
for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgEditValidator(tc.validatorAddr, description)
if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}
// test ValidateBasic for MsgDelegate
func TestMsgDelegate(t *testing.T) {
tests := []struct {
name string
delegatorAddr sdk.Address
validatorAddr sdk.Address
bond sdk.Coin
expectPass bool
}{
{"basic good", addrs[0], addrs[1], coinPos, true},
{"self bond", addrs[0], addrs[0], coinPos, true},
{"empty delegator", emptyAddr, addrs[0], coinPos, false},
{"empty validator", addrs[0], emptyAddr, coinPos, false},
{"empty bond", addrs[0], addrs[1], coinZero, false},
{"negative bond", addrs[0], addrs[1], coinNeg, false},
{"wrong staking token", addrs[0], addrs[1], coinPosNotAtoms, false},
}
for _, tc := range tests {
msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond)
if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}
// test ValidateBasic for MsgUnbond
func TestMsgUnbond(t *testing.T) {
tests := []struct {
name string
delegatorAddr sdk.Address
validatorAddr sdk.Address
shares string
expectPass bool
}{
{"max unbond", addrs[0], addrs[1], "MAX", true},
{"decimal unbond", addrs[0], addrs[1], "0.1", true},
{"negative decimal unbond", addrs[0], addrs[1], "-0.1", false},
{"zero unbond", addrs[0], addrs[1], "0.0", false},
{"invalid decimal", addrs[0], addrs[0], "sunny", false},
{"empty delegator", emptyAddr, addrs[0], "0.1", false},
{"empty validator", addrs[0], emptyAddr, "0.1", false},
}
for _, tc := range tests {
msg := NewMsgUnbond(tc.delegatorAddr, tc.validatorAddr, tc.shares)
if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}
// TODO introduce with go-amino
//func TestSerializeMsg(t *testing.T) {
//// make sure all types construct properly
//bondAmt := 1234321
//bond := sdk.Coin{Denom: "atom", Amount: int64(bondAmt)}
//tests := []struct {
//tx sdk.Msg
//}{
//{NewMsgCreateValidator(addrs[0], pks[0], bond, Description{})},
//{NewMsgEditValidator(addrs[0], Description{})},
//{NewMsgDelegate(addrs[0], addrs[1], bond)},
//{NewMsgUnbond(addrs[0], addrs[1], strconv.Itoa(bondAmt))},
//}
//for i, tc := range tests {
//var tx sdk.Tx
//bs := wire.BinaryBytes(tc.tx)
//err := wire.ReadBinaryBytes(bs, &tx)
//if assert.NoError(t, err, "%d", i) {
//assert.Equal(t, tc.tx, tx, "%d", i)
//}
//}
//}

Some files were not shown because too many files have changed in this diff Show More