Merge branch 'develop' into develop
This commit is contained in:
commit
50733f5c22
|
@ -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
|
||||
|
@ -103,7 +103,7 @@ jobs:
|
|||
|
||||
test_cover:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
|
|
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -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
|
||||
* [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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]]
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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
|
||||
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -100,13 +100,13 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
|
|||
config.TxIndex.IndexAllTags = true
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
logger = log.NewFilter(logger, log.AllowDebug())
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
privVal.Reset()
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
cdc = gapp.MakeCodec()
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
|
@ -146,6 +146,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (
|
|||
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
genesisState.StakeData.Pool.LooseTokens += 100
|
||||
}
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
|
|
|
@ -3,6 +3,7 @@ package app
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
@ -17,8 +18,8 @@ import (
|
|||
|
||||
var (
|
||||
// bonded tokens given to genesis validators/accounts
|
||||
freeFermionVal = sdk.NewInt(100)
|
||||
freeFermionsAcc = sdk.NewInt(50)
|
||||
freeFermionVal = int64(100)
|
||||
freeFermionsAcc = int64(50)
|
||||
)
|
||||
|
||||
// State to Unmarshal
|
||||
|
@ -124,7 +125,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.Address, name st
|
|||
|
||||
validator = tmtypes.GenesisValidator{
|
||||
PubKey: pk,
|
||||
Power: freeFermionVal.Int64(),
|
||||
Power: freeFermionVal,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -155,22 +156,33 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
|
|||
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
|
||||
accAuth.Coins = sdk.Coins{
|
||||
{genTx.Name + "Token", sdk.NewInt(1000)},
|
||||
{"steak", freeFermionsAcc},
|
||||
{"steak", sdk.NewInt(freeFermionsAcc)},
|
||||
}
|
||||
acc := NewGenesisAccount(&accAuth)
|
||||
genaccs[i] = acc
|
||||
stakeData.Pool.LooseUnbondedTokens = stakeData.Pool.LooseUnbondedTokens.Add(freeFermionsAcc) // increase the supply
|
||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionsAcc // increase the supply
|
||||
|
||||
// add the validator
|
||||
if len(genTx.Name) > 0 {
|
||||
desc := stake.NewDescription(genTx.Name, "", "", "")
|
||||
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
|
||||
validator.PoolShares = stake.NewBondedShares(sdk.NewRatFromInt(freeFermionVal))
|
||||
|
||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionVal // increase the supply
|
||||
|
||||
// add some new shares to the validator
|
||||
var issuedDelShares sdk.Rat
|
||||
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, freeFermionVal)
|
||||
stakeData.Validators = append(stakeData.Validators, validator)
|
||||
|
||||
// pool logic
|
||||
stakeData.Pool.BondedTokens = stakeData.Pool.BondedTokens.Add(freeFermionVal)
|
||||
stakeData.Pool.BondedShares = sdk.NewRatFromInt(stakeData.Pool.BondedTokens)
|
||||
// create the self-delegation from the issuedDelShares
|
||||
delegation := stake.Delegation{
|
||||
DelegatorAddr: validator.Owner,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: issuedDelShares,
|
||||
Height: 0,
|
||||
}
|
||||
|
||||
stakeData.Bonds = append(stakeData.Bonds, delegation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ func main() {
|
|||
stakecmd.GetCmdCreateValidator(cdc),
|
||||
stakecmd.GetCmdEditValidator(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
stakecmd.GetCmdUnbond("stake", cdc),
|
||||
stakecmd.GetCmdRedelegate("stake", cdc),
|
||||
slashingcmd.GetCmdUnrevoke(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
::
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
@ -37,8 +37,8 @@ processProvisions():
|
|||
|
||||
provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr)
|
||||
|
||||
pool.LooseUnbondedTokens += provisions
|
||||
feePool += LooseUnbondedTokens
|
||||
pool.LooseTokens += provisions
|
||||
feePool += LooseTokens
|
||||
|
||||
setPool(pool)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ information, etc.
|
|||
|
||||
```golang
|
||||
type Pool struct {
|
||||
LooseUnbondedTokens int64 // tokens not associated with any validator
|
||||
LooseTokens int64 // tokens not associated with any validator
|
||||
UnbondedTokens int64 // reserve of unbonded tokens held with validators
|
||||
UnbondingTokens int64 // tokens moving from bonded to unbonded pool
|
||||
BondedTokens int64 // reserve of bonded tokens
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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, "")
|
||||
}
|
|
@ -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{}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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...)
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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() {}
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package types
|
||||
|
||||
import ()
|
||||
|
||||
// Gas measured by the SDK
|
||||
type Gas = int64
|
||||
|
||||
|
|
102
types/int.go
102
types/int.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -59,8 +59,9 @@ type ValidatorSet interface {
|
|||
IterateValidatorsBonded(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
Validator(Context, Address) Validator // get a particular validator by owner address
|
||||
TotalPower(Context) Rat // total power of the validator set
|
||||
Validator(Context, Address) Validator // get a particular validator by owner address
|
||||
TotalPower(Context) Rat // total power of the validator set
|
||||
|
||||
Slash(Context, crypto.PubKey, int64, Rat) // slash the validator and delegators of the validator, specifying offence height & slash fraction
|
||||
Revoke(Context, crypto.PubKey) // revoke a validator
|
||||
Unrevoke(Context, crypto.PubKey) // unrevoke a validator
|
||||
|
|
|
@ -21,8 +21,8 @@ func (t Tags) AppendTag(k string, v []byte) Tags {
|
|||
}
|
||||
|
||||
// Append two lists of tags
|
||||
func (t Tags) AppendTags(a Tags) Tags {
|
||||
return append(t, a...)
|
||||
func (t Tags) AppendTags(tags Tags) Tags {
|
||||
return append(t, tags...)
|
||||
}
|
||||
|
||||
// Turn tags into KVPair list
|
||||
|
@ -51,3 +51,13 @@ func NewTags(tags ...interface{}) Tags {
|
|||
func MakeTag(k string, v []byte) Tag {
|
||||
return Tag{Key: []byte(k), Value: v}
|
||||
}
|
||||
|
||||
//__________________________________________________
|
||||
|
||||
// common tags
|
||||
var (
|
||||
TagAction = "action"
|
||||
TagSrcValidator = "source-validator"
|
||||
TagDstValidator = "destination-validator"
|
||||
TagDelegator = "delegator"
|
||||
)
|
||||
|
|
|
@ -31,9 +31,6 @@ type Tx interface {
|
|||
|
||||
// Gets the Msg.
|
||||
GetMsgs() []Msg
|
||||
|
||||
// Gets the memo.
|
||||
GetMemo() string
|
||||
}
|
||||
|
||||
//__________________________________________________________
|
||||
|
|
12
wire/wire.go
12
wire/wire.go
|
@ -35,3 +35,15 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
|||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
//__________________________________________________________________
|
||||
|
||||
// generic sealed codec to be used throughout sdk
|
||||
var Cdc *Codec
|
||||
|
||||
func init() {
|
||||
cdc := NewCodec()
|
||||
RegisterCrypto(cdc)
|
||||
Cdc = cdc
|
||||
//Cdc = cdc.Seal() // TODO uncomment once amino upgraded to 0.9.10
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,7 +57,11 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
|
||||
|
||||
stakeGenesis := stake.DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
|
||||
stake.InitGenesis(ctx, stakeKeeper, stakeGenesis)
|
||||
InitGenesis(ctx, keeper, DefaultGenesisState())
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,9 @@ func getEndBlocker(keeper stake.Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
stake.InitGenesis(ctx, keeper, stake.DefaultGenesisState())
|
||||
stakeGenesis := stake.DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
stake.InitGenesis(ctx, keeper, stakeGenesis)
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,41 +12,16 @@ const (
|
|||
// Default slashing codespace
|
||||
DefaultCodespace sdk.CodespaceType = 10
|
||||
|
||||
// Invalid validator
|
||||
CodeInvalidValidator CodeType = 201
|
||||
// Validator jailed
|
||||
CodeValidatorJailed CodeType = 202
|
||||
CodeInvalidValidator CodeType = 101
|
||||
CodeValidatorJailed CodeType = 102
|
||||
)
|
||||
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
|
||||
}
|
||||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
|
||||
}
|
||||
|
||||
func codeToDefaultMsg(code CodeType) string {
|
||||
switch code {
|
||||
case CodeInvalidValidator:
|
||||
return "invalid Validator"
|
||||
case CodeValidatorJailed:
|
||||
return "validator Jailed"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked")
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||
pool := sk.GetPool(ctx)
|
||||
require.Equal(t, int64(100), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(100), pool.BondedTokens)
|
||||
|
||||
// 51st block missed
|
||||
ctx = ctx.WithBlockHeight(height)
|
||||
|
@ -109,7 +109,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
|
||||
// validator should have been slashed
|
||||
pool = sk.GetPool(ctx)
|
||||
require.Equal(t, int64(99), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(99), pool.BondedTokens)
|
||||
|
||||
// validator start height should have been changed
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
|
||||
|
@ -167,5 +167,5 @@ func TestHandleNewValidator(t *testing.T) {
|
|||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||
pool := sk.GetPool(ctx)
|
||||
require.Equal(t, int64(100), pool.BondedTokens.Int64())
|
||||
require.Equal(t, int64(100), pool.BondedTokens)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// TODO remove dependencies on staking (should only refer to validator set type from sdk)
|
||||
|
||||
var (
|
||||
addrs = []sdk.Address{
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6160"),
|
||||
|
@ -61,7 +63,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep
|
|||
ck := bank.NewKeeper(accountMapper)
|
||||
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
|
||||
genesis := stake.DefaultGenesisState()
|
||||
genesis.Pool.LooseUnbondedTokens = initCoins.MulRaw(int64(len(addrs)))
|
||||
genesis.Pool.LooseTokens = initCoins.MulRaw(int64(len(addrs))).Int64()
|
||||
stake.InitGenesis(ctx, sk, genesis)
|
||||
for _, addr := range addrs {
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
|
@ -89,9 +91,9 @@ func testAddr(addr string) sdk.Address {
|
|||
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
|
||||
return stake.MsgCreateValidator{
|
||||
Description: stake.Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Description: stake.Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
SelfDelegation: sdk.Coin{"steak", amt},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ var (
|
|||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{sdk.NewCoin("foocoin", 10)}
|
||||
coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{sdk.NewCoin("foocoin", 0)},
|
||||
sdk.Coins{{"foocoin", sdk.NewInt(0)}},
|
||||
100000,
|
||||
}
|
||||
)
|
||||
|
@ -60,7 +60,9 @@ func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
|||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
InitGenesis(ctx, keeper, DefaultGenesisState())
|
||||
stakeGenesis := DefaultGenesisState()
|
||||
stakeGenesis.Pool.LooseTokens = 100000
|
||||
InitGenesis(ctx, keeper, stakeGenesis)
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
@ -93,8 +95,8 @@ func checkDelegation(t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
|
|||
func TestStakeMsgs(t *testing.T) {
|
||||
mapp, keeper := getMockApp(t)
|
||||
|
||||
genCoin := sdk.NewCoin("steak", 42)
|
||||
bondCoin := sdk.NewCoin("steak", 10)
|
||||
genCoin := sdk.Coin{"steak", sdk.NewInt(42)}
|
||||
bondCoin := sdk.Coin{"steak", sdk.NewInt(10)}
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
|
@ -148,10 +150,14 @@ func TestStakeMsgs(t *testing.T) {
|
|||
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Unbond
|
||||
// Begin Unbonding
|
||||
|
||||
unbondMsg := NewMsgUnbond(addr2, addr1, "MAX")
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unbondMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10))
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
|
||||
// delegation should exist anymore
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
|
||||
|
||||
// balance should be the same because bonding not yet complete
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
}
|
||||
|
|
|
@ -6,11 +6,14 @@ import (
|
|||
|
||||
// nolint
|
||||
const (
|
||||
FlagAddressDelegator = "address-delegator"
|
||||
FlagAddressValidator = "address-validator"
|
||||
FlagPubKey = "pubkey"
|
||||
FlagAmount = "amount"
|
||||
FlagShares = "shares"
|
||||
FlagAddressDelegator = "address-delegator"
|
||||
FlagAddressValidator = "address-validator"
|
||||
FlagAddressValidatorSrc = "addr-validator-source"
|
||||
FlagAddressValidatorDst = "addr-validator-dest"
|
||||
FlagPubKey = "pubkey"
|
||||
FlagAmount = "amount"
|
||||
FlagSharesAmount = "shares-amount"
|
||||
FlagSharesPercent = "shares-percent"
|
||||
|
||||
FlagMoniker = "moniker"
|
||||
FlagIdentity = "keybase-sig"
|
||||
|
@ -20,22 +23,26 @@ const (
|
|||
|
||||
// common flagsets to add to various functions
|
||||
var (
|
||||
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsPk = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
)
|
||||
|
||||
func init() {
|
||||
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220")
|
||||
fsAmount.String(FlagAmount, "1steak", "Amount of coins to bond")
|
||||
fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)")
|
||||
fsDescription.String(FlagMoniker, "", "validator name")
|
||||
fsDescription.String(FlagIdentity, "", "optional keybase signature")
|
||||
fsDescription.String(FlagWebsite, "", "optional website")
|
||||
fsDescription.String(FlagDetails, "", "optional details")
|
||||
fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal")
|
||||
fsShares.String(FlagSharesPercent, "", "Percent of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1")
|
||||
fsDescription.String(FlagMoniker, "[do-not-modify]", "validator name")
|
||||
fsDescription.String(FlagIdentity, "[do-not-modify]", "optional keybase signature")
|
||||
fsDescription.String(FlagWebsite, "[do-not-modify]", "optional website")
|
||||
fsDescription.String(FlagDetails, "[do-not-modify]", "optional details")
|
||||
fsValidator.String(FlagAddressValidator, "", "hex address of the validator")
|
||||
fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator")
|
||||
fsRedelegation.String(FlagAddressValidatorSrc, "", "hex address of the source validator")
|
||||
fsRedelegation.String(FlagAddressValidatorDst, "", "hex address of the destination validator")
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
|
@ -105,14 +105,14 @@ func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single delegation bond
|
||||
// get the command to query a single delegation
|
||||
func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegation",
|
||||
Short: "Query a delegations bond based on address and validator address",
|
||||
Short: "Query a delegation based on address and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
addr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -122,26 +122,26 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
key := stake.GetDelegationKey(delAddr, addr, cdc)
|
||||
key := stake.GetDelegationKey(delAddr, valAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the bond
|
||||
bond := new(stake.Delegation)
|
||||
// parse out the delegation
|
||||
delegation := new(stake.Delegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := bond.HumanReadableString()
|
||||
resp, err := delegation.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, bond)
|
||||
output, err := wire.MarshalJSONIndent(cdc, bond)
|
||||
cdc.MustUnmarshalBinary(res, delegation)
|
||||
output, err := wire.MarshalJSONIndent(cdc, delegation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the validators bonded to a delegation
|
||||
// get the command to query all the delegations made from one delegator
|
||||
func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegations [delegator-addr]",
|
||||
|
@ -196,3 +196,190 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single unbonding-delegation record
|
||||
func GetCmdQueryUnbondingDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegation",
|
||||
Short: "Query an unbonding-delegation record based on delegator and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
valAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := stake.GetUBDKey(delAddr, valAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the unbonding delegation
|
||||
ubd := new(stake.UnbondingDelegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := ubd.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, ubd)
|
||||
output, err := wire.MarshalJSONIndent(cdc, ubd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the unbonding-delegation records for a delegator
|
||||
func GetCmdQueryUnbondingDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegations [delegator-addr]",
|
||||
Short: "Query all unbonding-delegations records for one delegator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := stake.GetUBDsKey(delegatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the validators
|
||||
var ubds []stake.UnbondingDelegation
|
||||
for _, KV := range resKVs {
|
||||
var ubd stake.UnbondingDelegation
|
||||
cdc.MustUnmarshalBinary(KV.Value, &ubd)
|
||||
ubds = append(ubds, ubd)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, ubds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
|
||||
// TODO output with proofs / machine parseable etc.
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single unbonding-delegation record
|
||||
func GetCmdQueryRedelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegation",
|
||||
Short: "Query an unbonding-delegation record based on delegator and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
valSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delAddr, err := sdk.GetValAddressHex(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := stake.GetREDKey(delAddr, valSrcAddr, valDstAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the unbonding delegation
|
||||
red := new(stake.Redelegation)
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
resp, err := red.HumanReadableString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(resp)
|
||||
case "json":
|
||||
cdc.MustUnmarshalBinary(res, red)
|
||||
output, err := wire.MarshalJSONIndent(cdc, red)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the unbonding-delegation records for a delegator
|
||||
func GetCmdQueryRedelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbonding-delegations [delegator-addr]",
|
||||
Short: "Query all unbonding-delegations records for one delegator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := stake.GetREDsKey(delegatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the validators
|
||||
var reds []stake.Redelegation
|
||||
for _, KV := range resKVs {
|
||||
var red stake.Redelegation
|
||||
cdc.MustUnmarshalBinary(KV.Value, &red)
|
||||
reds = append(reds, red)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, reds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
|
||||
// TODO output with proofs / machine parseable etc.
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
|
@ -104,11 +105,11 @@ func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
// delegate command
|
||||
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegate",
|
||||
Short: "delegate coins to an existing validator",
|
||||
Short: "delegate liquid tokens to an validator",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
amount, err := sdk.ParseCoin(viper.GetString(FlagAmount))
|
||||
if err != nil {
|
||||
|
@ -143,33 +144,185 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
||||
func GetCmdRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbond",
|
||||
Short: "unbond shares from a validator",
|
||||
Use: "redelegate",
|
||||
Short: "redelegate illiquid tokens from one validator to another",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
GetCmdBeginRedelegate(storeName, cdc),
|
||||
GetCmdCompleteRedelegate(cdc),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// redelegate command
|
||||
func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "begin",
|
||||
Short: "begin redelegation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// check the shares before broadcasting
|
||||
sharesStr := viper.GetString(FlagShares)
|
||||
var shares sdk.Rat
|
||||
if sharesStr != "MAX" {
|
||||
var err error
|
||||
shares, err = sdk.NewRatFromDecimal(sharesStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !shares.GT(sdk.ZeroRat()) {
|
||||
return fmt.Errorf("shares must be positive integer or decimal (ex. 123, 1.23456789)")
|
||||
}
|
||||
var err error
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the shares amount
|
||||
sharesAmountStr := viper.GetString(FlagSharesAmount)
|
||||
sharesPercentStr := viper.GetString(FlagSharesPercent)
|
||||
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
|
||||
delegatorAddr, validatorSrcAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr, sharesAmount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsShares)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getShares(storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercentStr string,
|
||||
delegatorAddr, validatorAddr sdk.Address) (sharesAmount sdk.Rat, err error) {
|
||||
|
||||
switch {
|
||||
case sharesAmountStr != "" && sharesPercentStr != "":
|
||||
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
|
||||
case sharesAmountStr == "" && sharesPercentStr == "":
|
||||
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
|
||||
case sharesAmountStr != "":
|
||||
sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
if !sharesAmount.GT(sdk.ZeroRat()) {
|
||||
return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
|
||||
}
|
||||
case sharesPercentStr != "":
|
||||
var sharesPercent sdk.Rat
|
||||
sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) {
|
||||
return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
|
||||
}
|
||||
|
||||
// make a query to get the existing delegation shares
|
||||
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resQuery, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
return sharesAmount, err
|
||||
}
|
||||
var delegation stake.Delegation
|
||||
err = cdc.UnmarshalBinary(resQuery, &delegation)
|
||||
if err != nil {
|
||||
return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err)
|
||||
}
|
||||
|
||||
sharesAmount = sharesPercent.Mul(delegation.Shares)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// redelegate command
|
||||
func GetCmdCompleteRedelegate(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "complete",
|
||||
Short: "complete redelegation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
validatorSrcAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorSrc))
|
||||
validatorDstAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidatorDst))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgCompleteRedelegate(delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsRedelegation)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdUnbond(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbond",
|
||||
Short: "begin or complete unbonding shares from a validator",
|
||||
}
|
||||
cmd.AddCommand(
|
||||
GetCmdBeginUnbonding(storeName, cdc),
|
||||
GetCmdCompleteUnbonding(cdc),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdBeginUnbonding(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "begin",
|
||||
Short: "begin unbonding",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgUnbond(delegatorAddr, validatorAddr, sharesStr)
|
||||
// get the shares amount
|
||||
sharesAmountStr := viper.GetString(FlagSharesAmount)
|
||||
sharesPercentStr := viper.GetString(FlagSharesPercent)
|
||||
sharesAmount, err := getShares(storeName, cdc, sharesAmountStr, sharesPercentStr,
|
||||
delegatorAddr, validatorAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sharesAmount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -189,3 +342,35 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
|||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit validator command
|
||||
func GetCmdCompleteUnbonding(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "complete",
|
||||
Short: "complete unbonding",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
|
||||
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -13,18 +13,30 @@ import (
|
|||
)
|
||||
|
||||
func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/bonding_status/{validator}",
|
||||
bondingStatusHandlerFn(ctx, "stake", cdc),
|
||||
"/stake/{delegator}/delegation/{validator}",
|
||||
delegationHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/ubd/{validator}",
|
||||
ubdHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/{delegator}/red/{validator_src}/{validator_dst}",
|
||||
redHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc(
|
||||
"/stake/validators",
|
||||
validatorsHandlerFn(ctx, "stake", cdc),
|
||||
).Methods("GET")
|
||||
}
|
||||
|
||||
// http request handler to query delegator bonding status
|
||||
func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
// http request handler to query a delegation
|
||||
func delegationHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
|
@ -51,25 +63,147 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
|||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this bond
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var bond stake.Delegation
|
||||
err = cdc.UnmarshalBinary(res, &bond)
|
||||
var delegation stake.Delegation
|
||||
err = cdc.UnmarshalBinary(res, &delegation)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode bond. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(bond)
|
||||
output, err := cdc.MarshalJSON(delegation)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
// http request handler to query an unbonding-delegation
|
||||
func ubdHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
bech32delegator := vars["delegator"]
|
||||
bech32validator := vars["validator"]
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
key := stake.GetUBDKey(delegatorAddr, validatorAddr, cdc)
|
||||
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var ubd stake.UnbondingDelegation
|
||||
err = cdc.UnmarshalBinary(res, &ubd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode unbonding-delegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(ubd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
// http request handler to query an redelegation
|
||||
func redHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
bech32delegator := vars["delegator"]
|
||||
bech32validatorSrc := vars["validator_src"]
|
||||
bech32validatorDst := vars["validator_dst"]
|
||||
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(bech32validatorSrc)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(bech32validatorDst)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
key := stake.GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr, cdc)
|
||||
|
||||
res, err := ctx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't query redelegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this record
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var red stake.Redelegation
|
||||
err = cdc.UnmarshalBinary(res, &red)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode redelegation. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(red)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -24,31 +24,50 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k
|
|||
).Methods("POST")
|
||||
}
|
||||
|
||||
type msgDelegateInput struct {
|
||||
type msgDelegationsInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
type msgUnbondInput struct {
|
||||
type msgBeginRedelegateInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
|
||||
SharesAmount string `json:"shares"`
|
||||
}
|
||||
type msgCompleteRedelegateInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
|
||||
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
|
||||
}
|
||||
type msgBeginUnbondingInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
SharesAmount string `json:"shares"`
|
||||
}
|
||||
type msgCompleteUnbondingInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
type editDelegationsBody struct {
|
||||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegate []msgDelegateInput `json:"delegate"`
|
||||
Unbond []msgUnbondInput `json:"unbond"`
|
||||
// request body for edit delegations
|
||||
type EditDelegationsBody struct {
|
||||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegations []msgDelegationsInput `json:"delegations"`
|
||||
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
|
||||
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
|
||||
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
|
||||
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
|
||||
}
|
||||
|
||||
func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var m editDelegationsBody
|
||||
var m EditDelegationsBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
@ -70,24 +89,29 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
|||
}
|
||||
|
||||
// build messages
|
||||
messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond))
|
||||
messages := make([]sdk.Msg, len(m.Delegations)+
|
||||
len(m.BeginRedelegates)+
|
||||
len(m.CompleteRedelegates)+
|
||||
len(m.BeginUnbondings)+
|
||||
len(m.CompleteUnbondings))
|
||||
|
||||
i := 0
|
||||
for _, msg := range m.Delegate {
|
||||
for _, msg := range m.Delegations {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("must use own delegator address"))
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgDelegate{
|
||||
|
@ -97,28 +121,131 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
|||
}
|
||||
i++
|
||||
}
|
||||
for _, msg := range m.Unbond {
|
||||
|
||||
for _, msg := range m.BeginRedelegates {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode delegator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgBeginRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
SharesAmount: shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.CompleteRedelegates {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorSrcAddr, err := sdk.GetValAddressBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorDstAddr, err := sdk.GetValAddressBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgCompleteRedelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.BeginUnbondings {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("couldn't decode validator. Error: %s", err.Error())))
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
shares, err := sdk.NewRatFromDecimal(msg.SharesAmount)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgBeginUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
SharesAmount: shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
for _, msg := range m.CompleteUnbondings {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("must use own delegator address"))
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = stake.MsgUnbond{
|
||||
messages[i] = stake.MsgCompleteUnbonding{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: msg.Shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Delegation represents the bond with tokens held by an account. It is
|
||||
// owned by one delegator, and is associated with the voting power of one
|
||||
// pubKey.
|
||||
type Delegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
func (b Delegation) equal(b2 Delegation) bool {
|
||||
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b.ValidatorAddr, b2.ValidatorAddr) &&
|
||||
b.Height == b2.Height &&
|
||||
b.Shares.Equal(b2.Shares)
|
||||
}
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Delegation = Delegation{}
|
||||
|
||||
// nolint - for sdk.Delegation
|
||||
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
|
||||
func (b Delegation) GetValidator() sdk.Address { return b.ValidatorAddr }
|
||||
func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
|
||||
|
||||
//Human Friendly pretty printer
|
||||
func (b Delegation) HumanReadableString() (string, error) {
|
||||
bechAcc, err := sdk.Bech32ifyAcc(b.DelegatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bechVal, err := sdk.Bech32ifyAcc(b.ValidatorAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp := "Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", bechAcc)
|
||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||
resp += fmt.Sprintf("Shares: %s", b.Shares.String())
|
||||
resp += fmt.Sprintf("Height: %d", b.Height)
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
// nolint
|
||||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type CodeType = sdk.CodeType
|
||||
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 4
|
||||
|
||||
// Gaia errors reserve 200 ~ 299.
|
||||
CodeInvalidValidator CodeType = 201
|
||||
CodeInvalidBond CodeType = 202
|
||||
CodeInvalidInput CodeType = 203
|
||||
CodeValidatorJailed CodeType = 204
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
// NOTE: Don't stringer this, we'll put better messages in later.
|
||||
func codeToDefaultMsg(code CodeType) string {
|
||||
switch code {
|
||||
case CodeInvalidValidator:
|
||||
return "invalid Validator"
|
||||
case CodeInvalidBond:
|
||||
return "invalid Bond"
|
||||
case CodeInvalidInput:
|
||||
return "invalid Input"
|
||||
case CodeUnauthorized:
|
||||
return "unauthorized"
|
||||
case CodeInternal:
|
||||
return "internal Error"
|
||||
case CodeUnknownRequest:
|
||||
return "unknown request"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Error constructors
|
||||
|
||||
func ErrNotEnoughBondShares(codespace sdk.CodespaceType, shares string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
|
||||
}
|
||||
func ErrValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "cannot bond to an empty validator")
|
||||
}
|
||||
func ErrBadBondingDenom(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, "invalid coin denomination")
|
||||
}
|
||||
func ErrBadBondingAmount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, "amount must be > 0")
|
||||
}
|
||||
func ErrNoBondingAcct(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "no bond account for this (address, validator) pair")
|
||||
}
|
||||
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "commission must be positive")
|
||||
}
|
||||
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
|
||||
}
|
||||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "delegator does not exist for that address")
|
||||
}
|
||||
func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator")
|
||||
}
|
||||
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator for this address is currently revoked")
|
||||
}
|
||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "missing signature")
|
||||
}
|
||||
func ErrBondNotNominated(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "cannot bond to non-nominated account")
|
||||
}
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "delegator does not contain validator bond")
|
||||
}
|
||||
func ErrInsufficientFunds(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidInput, "insufficient bond shares")
|
||||
}
|
||||
func ErrBadShares(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidInput, "bad shares provided as input, must be MAX or decimal")
|
||||
}
|
||||
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "error removing validator")
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// TODO group with code from x/bank/errors.go
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
}
|
|
@ -4,63 +4,39 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
Validators []Validator `json:"validators"`
|
||||
Bonds []Delegation `json:"bonds"`
|
||||
}
|
||||
|
||||
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
|
||||
return GenesisState{
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: InitialPool(),
|
||||
Params: DefaultParams(),
|
||||
}
|
||||
}
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
k.setPool(ctx, data.Pool)
|
||||
k.setNewParams(ctx, data.Params)
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
|
||||
keeper.SetPool(ctx, data.Pool)
|
||||
keeper.SetNewParams(ctx, data.Params)
|
||||
keeper.InitIntraTxCounter(ctx)
|
||||
for _, validator := range data.Validators {
|
||||
|
||||
// set validator
|
||||
k.setValidator(ctx, validator)
|
||||
keeper.SetValidator(ctx, validator)
|
||||
|
||||
// manually set indexes for the first time
|
||||
k.setValidatorByPubKeyIndex(ctx, validator)
|
||||
k.setValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
|
||||
keeper.SetValidatorBondedIndex(ctx, validator)
|
||||
}
|
||||
}
|
||||
for _, bond := range data.Bonds {
|
||||
k.setDelegation(ctx, bond)
|
||||
keeper.SetDelegation(ctx, bond)
|
||||
}
|
||||
k.updateBondedValidatorsFull(ctx, store)
|
||||
keeper.UpdateBondedValidatorsFull(ctx)
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
validators := k.getAllValidators(ctx)
|
||||
bonds := k.getAllDelegations(ctx)
|
||||
return GenesisState{
|
||||
func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
validators := keeper.GetAllValidators(ctx)
|
||||
bonds := keeper.GetAllDelegations(ctx)
|
||||
return types.GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
|
@ -69,8 +45,8 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
|||
}
|
||||
|
||||
// WriteValidators - output current validator set
|
||||
func WriteValidators(ctx sdk.Context, k Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
vals = append(vals, tmtypes.GenesisValidator{
|
||||
PubKey: validator.GetPubKey(),
|
||||
Power: validator.GetPower().Evaluate(),
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/tags"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
func NewHandler(k Keeper) sdk.Handler {
|
||||
func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
// NOTE msg already has validate basic run
|
||||
switch msg := msg.(type) {
|
||||
case MsgCreateValidator:
|
||||
case types.MsgCreateValidator:
|
||||
return handleMsgCreateValidator(ctx, msg, k)
|
||||
case MsgEditValidator:
|
||||
case types.MsgEditValidator:
|
||||
return handleMsgEditValidator(ctx, msg, k)
|
||||
case MsgDelegate:
|
||||
case types.MsgDelegate:
|
||||
return handleMsgDelegate(ctx, msg, k)
|
||||
case MsgUnbond:
|
||||
return handleMsgUnbond(ctx, msg, k)
|
||||
case types.MsgBeginRedelegate:
|
||||
return handleMsgBeginRedelegate(ctx, msg, k)
|
||||
case types.MsgCompleteRedelegate:
|
||||
return handleMsgCompleteRedelegate(ctx, msg, k)
|
||||
case types.MsgBeginUnbonding:
|
||||
return handleMsgBeginUnbonding(ctx, msg, k)
|
||||
case types.MsgCompleteUnbonding:
|
||||
return handleMsgCompleteUnbonding(ctx, msg, k)
|
||||
default:
|
||||
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
|
||||
}
|
||||
|
@ -26,25 +34,25 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
}
|
||||
|
||||
// Called every block, process inflation, update validator set
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
|
||||
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// Process Validator Provisions
|
||||
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
|
||||
// Process types.Validator Provisions
|
||||
blockTime := ctx.BlockHeader().Time
|
||||
if pool.InflationLastTime+blockTime >= 3600 {
|
||||
pool.InflationLastTime = blockTime
|
||||
pool = k.processProvisions(ctx)
|
||||
pool = k.ProcessProvisions(ctx)
|
||||
}
|
||||
|
||||
// save the params
|
||||
k.setPool(ctx, pool)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// reset the intra-transaction counter
|
||||
k.setIntraTxCounter(ctx, 0)
|
||||
k.SetIntraTxCounter(ctx, 0)
|
||||
|
||||
// calculate validator set changes
|
||||
ValidatorUpdates = k.getTendermintUpdates(ctx)
|
||||
k.clearTendermintUpdates(ctx)
|
||||
ValidatorUpdates = k.GetTendermintUpdates(ctx)
|
||||
k.ClearTendermintUpdates(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -53,212 +61,150 @@ func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
|
|||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k Keeper) sdk.Result {
|
||||
func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if found {
|
||||
return ErrValidatorExistsAddr(k.codespace).Result()
|
||||
return ErrValidatorAlreadyExists(k.Codespace()).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
if msg.SelfDelegation.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadDenom(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
|
||||
k.setValidator(ctx, validator)
|
||||
k.setValidatorByPubKeyIndex(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("createValidator"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
)
|
||||
k.SetValidator(ctx, validator)
|
||||
k.SetValidatorByPubKeyIndex(ctx, validator)
|
||||
|
||||
// move coins from the msg.Address account to a (self-bond) delegator account
|
||||
// move coins from the msg.Address account to a (self-delegation) delegator account
|
||||
// the validator account and global shares are updated within here
|
||||
delegateTags, err := delegate(ctx, k, msg.ValidatorAddr, msg.Bond, validator)
|
||||
_, err := k.Delegate(ctx, msg.ValidatorAddr, msg.SelfDelegation, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
tags = tags.AppendTags(delegateTags)
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionCreateValidator,
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
tags.Moniker, []byte(msg.Description.Moniker),
|
||||
tags.Identity, []byte(msg.Description.Identity),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgEditValidator(ctx sdk.Context, msg MsgEditValidator, k Keeper) sdk.Result {
|
||||
func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// validator must already be registered
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
return ErrNoValidatorFound(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
// XXX move to types
|
||||
// replace all editable fields (clients should autofill existing values)
|
||||
validator.Description.Moniker = msg.Description.Moniker
|
||||
validator.Description.Identity = msg.Description.Identity
|
||||
validator.Description.Website = msg.Description.Website
|
||||
validator.Description.Details = msg.Description.Details
|
||||
description, err := validator.Description.UpdateDescription(msg.Description)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
validator.Description = description
|
||||
|
||||
k.updateValidator(ctx, validator)
|
||||
k.UpdateValidator(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("editValidator"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
tags.Action, tags.ActionEditValidator,
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
tags.Moniker, []byte(description.Moniker),
|
||||
tags.Identity, []byte(description.Identity),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
||||
func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result {
|
||||
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
return ErrNoValidatorFound(k.Codespace()).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
return ErrBadDenom(k.Codespace()).Result()
|
||||
}
|
||||
if validator.Revoked == true {
|
||||
return ErrValidatorRevoked(k.codespace).Result()
|
||||
return ErrValidatorRevoked(k.Codespace()).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
}
|
||||
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, validator)
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Bond, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionDelegate,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
// common functionality between handlers
|
||||
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
||||
bondAmt sdk.Coin, validator Validator) (sdk.Tags, sdk.Error) {
|
||||
|
||||
// Get or create the delegator bond
|
||||
bond, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
if !found {
|
||||
bond = Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
pool := k.GetPool(ctx)
|
||||
_, _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
|
||||
err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err.Result()
|
||||
}
|
||||
validator, pool, newShares := validator.addTokensFromDel(pool, bondAmt.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
|
||||
k.setPool(ctx, pool)
|
||||
k.setDelegation(ctx, bond)
|
||||
k.updateValidator(ctx, validator)
|
||||
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "validator", validator.Owner.Bytes())
|
||||
return tags, nil
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionBeginUnbonding,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||
func handleMsgCompleteUnbonding(ctx sdk.Context, msg types.MsgCompleteUnbonding, k keeper.Keeper) sdk.Result {
|
||||
|
||||
// check if bond has any shares in it unbond
|
||||
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoDelegatorForAddress(k.codespace).Result()
|
||||
err := k.CompleteUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
var delShares sdk.Rat
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, ActionCompleteUnbonding,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
|
||||
// test that there are enough shares to unbond
|
||||
if msg.Shares == "MAX" {
|
||||
if !bond.Shares.GT(sdk.ZeroRat()) {
|
||||
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
|
||||
}
|
||||
} else {
|
||||
var err sdk.Error
|
||||
delShares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
if bond.Shares.LT(delShares) {
|
||||
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
|
||||
}
|
||||
}
|
||||
|
||||
// get validator
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
|
||||
if msg.Shares == "MAX" {
|
||||
delShares = bond.Shares
|
||||
}
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
bond.Shares = bond.Shares.Sub(delShares)
|
||||
|
||||
// remove the bond
|
||||
revokeValidator := false
|
||||
if bond.Shares.IsZero() {
|
||||
|
||||
// if the bond is the owner of the validator then
|
||||
// trigger a revoke validator
|
||||
if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
|
||||
validator.Revoked == false {
|
||||
revokeValidator = true
|
||||
}
|
||||
|
||||
k.removeDelegation(ctx, bond)
|
||||
} else {
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
k.setDelegation(ctx, bond)
|
||||
}
|
||||
|
||||
// Add the coins
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, returnAmount := validator.removeDelShares(pool, delShares)
|
||||
k.setPool(ctx, pool)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||
|
||||
/////////////////////////////////////
|
||||
// revoke validator if necessary
|
||||
if revokeValidator {
|
||||
validator.Revoked = true
|
||||
}
|
||||
|
||||
validator = k.updateValidator(ctx, validator)
|
||||
|
||||
if validator.DelegatorShares.IsZero() {
|
||||
k.removeValidator(ctx, validator.Owner)
|
||||
}
|
||||
|
||||
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes())
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
|
||||
err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
|
||||
msg.ValidatorDstAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionBeginRedelegation,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgCompleteRedelegate(ctx sdk.Context, msg types.MsgCompleteRedelegate, k keeper.Keeper) sdk.Result {
|
||||
err := k.CompleteRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr, msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
tags := sdk.NewTags(
|
||||
tags.Action, tags.ActionCompleteRedelegation,
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
||||
)
|
||||
return sdk.Result{Tags: tags}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -10,38 +9,48 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
keep "github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {
|
||||
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Description: Description{},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
SelfDelegation: sdk.Coin{"steak", sdk.NewInt(amt)},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt sdk.Int) MsgDelegate {
|
||||
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
Bond: sdk.Coin{"steak", sdk.NewInt(amt)},
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve params which are instant
|
||||
func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params {
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 0
|
||||
keeper.SetParams(ctx, params)
|
||||
return params
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
func TestValidatorByPowerIndex(t *testing.T) {
|
||||
validatorAddr, validatorAddr3 := addrs[0], addrs[1]
|
||||
validatorAddr, validatorAddr3 := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
initBond := sdk.NewInt(1000000)
|
||||
initBondStr := "1000"
|
||||
ctx, _, keeper := createTestInput(t, false, initBond)
|
||||
initBond := int64(1000000)
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
|
@ -49,7 +58,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
|||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
require.Equal(t, initBond.Int64(), gotBond,
|
||||
require.Equal(t, initBond, gotBond,
|
||||
"initBond: %v\ngotBond: %v\nbond: %v\n",
|
||||
initBond, gotBond, bond)
|
||||
|
||||
|
@ -57,60 +66,62 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
|||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool := keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
power := keep.GetValidatorsByPowerIndexKey(validator, pool)
|
||||
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||
|
||||
// create a second validator keep it bonded
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, pks[2], sdk.NewInt(1000000))
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000))
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// slash and revoke the first validator
|
||||
keeper.Slash(ctx, pks[0], 0, sdk.NewRat(1, 2))
|
||||
keeper.Revoke(ctx, pks[0])
|
||||
keeper.Slash(ctx, keep.PKs[0], 0, sdk.NewRat(1, 2))
|
||||
keeper.Revoke(ctx, keep.PKs[0])
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded
|
||||
require.Equal(t, int64(500000), validator.PoolShares.Amount.Evaluate()) // ensure is unbonded
|
||||
|
||||
// the old power record should have been deleted as the power changed
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||
|
||||
// but the new power record should have been created
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool = keeper.GetPool(ctx)
|
||||
power2 := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power2))
|
||||
power2 := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
|
||||
|
||||
// inflate a bunch
|
||||
for i := 0; i < 20000; i++ {
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool = keeper.ProcessProvisions(ctx)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
// now the new record power index should be the same as the original record
|
||||
power3 := GetValidatorsByPowerKey(validator, pool)
|
||||
power3 := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.Equal(t, power2, power3)
|
||||
|
||||
// unbond self-delegation
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "MAX")
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\ninitBondStr: %v\n", got, msgUnbond, initBondStr)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(1000000))
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
|
||||
// verify that by power key nolonger exists
|
||||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power3))
|
||||
assert.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power3))
|
||||
}
|
||||
|
||||
func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
|
||||
validatorAddr := addrs[0]
|
||||
pk := pks[0]
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, sdk.NewInt(10))
|
||||
validatorAddr := keep.Addrs[0]
|
||||
pk := keep.PKs[0]
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "%v", got)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
|
@ -124,41 +135,41 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
|||
assert.Equal(t, Description{}, validator.Description)
|
||||
|
||||
// one validator cannot bond twice
|
||||
msgCreateValidator.PubKey = pks[1]
|
||||
msgCreateValidator.PubKey = keep.PKs[1]
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.False(t, got.IsOK(), "%v", got)
|
||||
}
|
||||
|
||||
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
bondAmount := sdk.NewInt(10)
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
bondAmount := int64(10)
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
// first create validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], bondAmount)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.Equal(t, bondAmount, validator.DelegatorShares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().EvaluateInt(), "validator: %v", validator)
|
||||
assert.Equal(t, bondAmount, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().Evaluate(), "validator: %v", validator)
|
||||
|
||||
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, bondAmount, bond.Shares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, bond.Shares.Evaluate())
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
|
||||
assert.Equal(t, bondAmount, pool.BondedShares.EvaluateInt())
|
||||
assert.Equal(t, bondAmount, pool.BondedShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, pool.BondedTokens)
|
||||
|
||||
// just send the same msgbond multiple times
|
||||
|
@ -180,14 +191,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
|
||||
|
||||
expBond := sdk.NewInt(int64(i + 1)).Mul(bondAmount)
|
||||
expDelegatorShares := sdk.NewInt(int64(i + 2)).Mul(bondAmount) // (1 self delegation)
|
||||
expDelegatorAcc := initBond.Sub(expBond)
|
||||
expBond := int64(i+1) * bondAmount
|
||||
expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorAcc := sdk.NewInt(initBond - expBond)
|
||||
|
||||
require.Equal(t, bond.Height, int64(i), "Incorrect bond height")
|
||||
|
||||
gotBond := bond.Shares.EvaluateInt()
|
||||
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
|
@ -203,14 +214,14 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator, delegate
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
|
@ -220,16 +231,19 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.EvaluateInt())
|
||||
assert.Equal(t, initBond.MulRaw(2), validator.PoolShares.Bonded().EvaluateInt())
|
||||
assert.Equal(t, initBond*2, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, initBond*2, validator.PoolShares.Bonded().Evaluate())
|
||||
|
||||
// just send the same msgUnbond multiple times
|
||||
// TODO use decimals here
|
||||
unbondShares, unbondSharesStr := int64(10), "10"
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
unbondShares := sdk.NewRat(10)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
numUnbonds := 5
|
||||
for i := 0; i < numUnbonds; i++ {
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
|
@ -238,12 +252,12 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
|
||||
expBond := initBond.SubRaw(int64(i+1) * unbondShares)
|
||||
expDelegatorShares := initBond.MulRaw(2).SubRaw(int64(i+1) * unbondShares)
|
||||
expDelegatorAcc := initBond.Sub(expBond)
|
||||
expBond := initBond - int64(i+1)*unbondShares.Evaluate()
|
||||
expDelegatorShares := 2*initBond - int64(i+1)*unbondShares.Evaluate()
|
||||
expDelegatorAcc := sdk.NewInt(initBond - expBond)
|
||||
|
||||
gotBond := bond.Shares.EvaluateInt()
|
||||
gotDelegatorShares := validator.DelegatorShares.EvaluateInt()
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
|
@ -263,41 +277,42 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
//1<<63 + 1, // more than int64
|
||||
1<<63 - 1,
|
||||
1 << 31,
|
||||
initBond.Int64(),
|
||||
initBond,
|
||||
}
|
||||
for _, c := range errorCases {
|
||||
unbondShares := strconv.Itoa(int(c))
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares := sdk.NewRat(int64(c))
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.False(t, got.IsOK(), "expected unbond msg to fail")
|
||||
}
|
||||
|
||||
leftBonded := initBond.SubRaw(unbondShares * int64(numUnbonds))
|
||||
leftBonded := initBond - int64(numUnbonds)*unbondShares.Evaluate()
|
||||
|
||||
// should be unable to unbond one more than we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()) + 1)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares = sdk.NewRat(leftBonded + 1)
|
||||
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
assert.False(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares.String(), leftBonded)
|
||||
|
||||
// should be able to unbond just what we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded.Int64()))
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
unbondShares = sdk.NewRat(leftBonded)
|
||||
msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares, leftBonded)
|
||||
}
|
||||
|
||||
func TestMultipleMsgCreateValidator(t *testing.T) {
|
||||
initBond := sdk.NewInt(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
validatorAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
validatorAddrs := []sdk.Address{keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]}
|
||||
|
||||
// bond them all
|
||||
for i, validatorAddr := range validatorAddrs {
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[i], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[i], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
|
@ -305,7 +320,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
validators := keeper.GetValidators(ctx, 100)
|
||||
require.Equal(t, (i + 1), len(validators))
|
||||
val := validators[i]
|
||||
balanceExpd := initBond.SubRaw(10)
|
||||
balanceExpd := sdk.NewInt(initBond - 10)
|
||||
balanceGot := accMapper.GetAccount(ctx, val.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators)
|
||||
require.Equal(t, 10, int(val.DelegatorShares.Evaluate()), "expected %d shares, got %d", 10, val.DelegatorShares)
|
||||
|
@ -316,8 +331,11 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
for i, validatorAddr := range validatorAddrs {
|
||||
validatorPre, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "10") // self-delegation
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) // self-delegation
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
|
@ -328,24 +346,25 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
|||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
expBalance := initBond
|
||||
expBalance := sdk.NewInt(initBond)
|
||||
gotBalance := accMapper.GetAccount(ctx, validatorPre.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleMsgDelegate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
validatorAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, delegatorAddrs := keep.Addrs[0], keep.Addrs[1:]
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
//first make a validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
|
||||
// delegate multiple parties
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
|
@ -357,8 +376,11 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
|
||||
// unbond them all
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
|
@ -368,37 +390,173 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRevokeValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(1000))
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], sdk.NewInt(10))
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// bond a delegator
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10))
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||
|
||||
validator, _ := keeper.GetValidator(ctx, validatorAddr)
|
||||
|
||||
// unbond the validators bond portion
|
||||
msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.True(t, validator.Revoked)
|
||||
require.True(t, validator.Revoked, "%v", validator)
|
||||
|
||||
// test that this address cannot yet be bonded too because is revoked
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
assert.False(t, got.IsOK(), "expected error, got %v", got)
|
||||
|
||||
// test that the delegator can still withdraw their bonds
|
||||
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// verify that the pubkey can now be reused
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||
}
|
||||
|
||||
func TestUnbondingPeriod(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr := keep.Addrs[0]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 7
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin unbonding
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10))
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// cannot complete unbonding at same time
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, !got.IsOK(), "expected no error")
|
||||
|
||||
// cannot complete unbonding at time 6 seconds later
|
||||
origHeader := ctx.BlockHeader()
|
||||
headerTime6 := origHeader
|
||||
headerTime6.Time += 6
|
||||
ctx = ctx.WithBlockHeader(headerTime6)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, !got.IsOK(), "expected no error")
|
||||
|
||||
// can complete unbonding at time 7 seconds later
|
||||
headerTime7 := origHeader
|
||||
headerTime7.Time += 7
|
||||
ctx = ctx.WithBlockHeader(headerTime7)
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
||||
func TestRedelegationPeriod(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, validatorAddr2 := keep.Addrs[0], keep.Addrs[1]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 7
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// cannot complete redelegation at same time
|
||||
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error")
|
||||
|
||||
// cannot complete redelegation at time 6 seconds later
|
||||
origHeader := ctx.BlockHeader()
|
||||
headerTime6 := origHeader
|
||||
headerTime6.Time += 6
|
||||
ctx = ctx.WithBlockHeader(headerTime6)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error")
|
||||
|
||||
// can complete redelegation at time 7 seconds later
|
||||
headerTime7 := origHeader
|
||||
headerTime7.Time += 7
|
||||
ctx = ctx.WithBlockHeader(headerTime7)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
||||
func TestTransitiveRedelegation(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, validatorAddr2, validatorAddr3 := keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 0
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// cannot redelegation to next validator while first delegation exists
|
||||
msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
|
||||
|
||||
// complete first redelegation
|
||||
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
|
||||
// now should be able to redelegate from the second validator to the third
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
|
|
@ -1,859 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// keeper of the staking store
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.Keeper
|
||||
|
||||
// codespace
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
keeper := Keeper{
|
||||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
codespace: codespace,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// get a single validator
|
||||
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return k.getValidator(store, addr)
|
||||
}
|
||||
|
||||
// get a single validator by pubkey
|
||||
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
|
||||
if addr == nil {
|
||||
return validator, false
|
||||
}
|
||||
return k.getValidator(store, addr)
|
||||
}
|
||||
|
||||
// get a single validator (reuse store)
|
||||
func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) {
|
||||
b := store.Get(GetValidatorKey(addr))
|
||||
if b == nil {
|
||||
return validator, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &validator)
|
||||
return validator, true
|
||||
}
|
||||
|
||||
// set the main record holding validator details
|
||||
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set main store
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bz)
|
||||
}
|
||||
|
||||
func (k Keeper) setValidatorByPubKeyIndex(ctx sdk.Context, validator Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set pointer by pubkey
|
||||
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
|
||||
}
|
||||
|
||||
func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner)
|
||||
}
|
||||
|
||||
// used in testing
|
||||
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
||||
|
||||
// Get the set of all validators with no limits, used during genesis dump
|
||||
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators = append(validators, validator)
|
||||
iterator.Next()
|
||||
}
|
||||
return validators
|
||||
}
|
||||
|
||||
// Get the set of all validators, retrieve a maxRetrieve number of records
|
||||
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators Validators) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
validators = make([]Validator, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators[i] = validator
|
||||
iterator.Next()
|
||||
}
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// get the group of the bonded validators
|
||||
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators = make([]Validator, maxValidators)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
|
||||
i := 0
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
// sanity check
|
||||
if i > int(maxValidators-1) {
|
||||
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// get the group of bonded validators sorted by power-rank
|
||||
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators := make([]Validator, maxValidators)
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerKey) // largest to smallest
|
||||
i := 0
|
||||
for {
|
||||
if !iterator.Valid() || i > int(maxValidators-1) {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
// Reached to revoked validators, stop iterating
|
||||
if validator.Revoked {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
if validator.Status() == sdk.Bonded {
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// Accumulated updates to the active/bonded validator set for tendermint
|
||||
|
||||
// get the most recently updated validators
|
||||
func (k Keeper) getTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val abci.Validator
|
||||
k.cdc.MustUnmarshalBinary(valBytes, &val)
|
||||
updates = append(updates, val)
|
||||
}
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries after applied to Tendermint
|
||||
func (k Keeper) clearTendermintUpdates(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// delete subspace
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// perfom all the nessisary steps for when a validator changes its power
|
||||
// updates all validator stores as well as tendermint update store
|
||||
// may kick out validators if new validator is entering the bonded validator group
|
||||
func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.getPool(store)
|
||||
ownerAddr := validator.Owner
|
||||
|
||||
// always update the main list ordered by owner address before exiting
|
||||
defer func() {
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(ownerAddr), bz)
|
||||
}()
|
||||
|
||||
// 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, ¶ms)
|
||||
return
|
||||
}
|
||||
|
||||
// Need a distinct function because setParams depends on an existing previous
|
||||
// record of params to exist (to check if maxValidators has changed) - and we
|
||||
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
|
||||
// first params set it will panic.
|
||||
func (k Keeper) setNewParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
// Public version of setNewParams
|
||||
func (k Keeper) SetNewParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
func (k Keeper) setParams(ctx sdk.Context, params Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
exParams := k.getParams(store)
|
||||
|
||||
// if max validator count changes, must recalculate validator set
|
||||
if exParams.MaxValidators != params.MaxValidators {
|
||||
k.updateBondedValidatorsFull(ctx, store)
|
||||
}
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the pool
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return k.getPool(store)
|
||||
}
|
||||
func (k Keeper) getPool(store sdk.KVStore) (pool Pool) {
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
panic("Stored pool should not have been nil")
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &pool)
|
||||
return
|
||||
}
|
||||
|
||||
func (k Keeper) setPool(ctx sdk.Context, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
// Public version of setpool
|
||||
func (k Keeper) SetPool(ctx sdk.Context, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) getIntraTxCounter(ctx sdk.Context) int16 {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
var counter int16
|
||||
k.cdc.MustUnmarshalBinary(b, &counter)
|
||||
return counter
|
||||
}
|
||||
|
||||
// set the current in-block validator operation counter
|
||||
func (k Keeper) setIntraTxCounter(ctx sdk.Context, counter int16) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(counter)
|
||||
store.Set(IntraTxCounterKey, bz)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current validator on the cliff
|
||||
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorCliffKey)
|
||||
}
|
||||
|
||||
// get the current power of the validator on the cliff
|
||||
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorPowerCliffKey)
|
||||
}
|
||||
|
||||
// set the current validator and power of the validator on the cliff
|
||||
func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := GetValidatorsByPowerKey(validator, pool)
|
||||
store.Set(ValidatorPowerCliffKey, bz)
|
||||
store.Set(ValidatorCliffKey, validator.Owner)
|
||||
}
|
||||
|
||||
// clear the current validator and power of the validator on the cliff
|
||||
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(ValidatorPowerCliffKey)
|
||||
store.Delete(ValidatorCliffKey)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements ValidatorSet
|
||||
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var validator Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
address := iterator.Value()
|
||||
validator, found := k.getValidator(store, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// get the sdk.validator for a particular address
|
||||
func (k Keeper) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
|
||||
val, found := k.GetValidator(ctx, addr)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// total power from the bond
|
||||
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
||||
pool := k.GetPool(ctx)
|
||||
return pool.BondedShares
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements DelegationSet
|
||||
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// get the delegation for a particular set of delegator and validator addresses
|
||||
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
|
||||
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
// Returns self as it is both a validatorset and delegationset
|
||||
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
|
||||
return k
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := GetDelegationsKey(delAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, key)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var delegation Delegation
|
||||
k.cdc.MustUnmarshalBinary(bz, &delegation)
|
||||
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// slash a validator
|
||||
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
|
||||
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("attempted to slash a nonexistent validator with address %s", pubkey.Address()))
|
||||
}
|
||||
sharesToRemove := val.PoolShares.Amount.Mul(fraction)
|
||||
pool := k.GetPool(ctx)
|
||||
val, pool, burned := val.removePoolShares(pool, sharesToRemove)
|
||||
k.setPool(ctx, pool) // update the pool
|
||||
k.updateValidator(ctx, val) // update the validator, possibly kicking it out
|
||||
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %v tokens", pubkey.Address(), fraction, sharesToRemove, burned))
|
||||
return
|
||||
}
|
||||
|
||||
// revoke a validator
|
||||
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("validator with pubkey %s not found, cannot revoke", pubkey))
|
||||
}
|
||||
val.Revoked = true
|
||||
k.updateValidator(ctx, val) // update the validator, now revoked
|
||||
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// unrevoke a validator
|
||||
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("validator with pubkey %s not found, cannot unrevoke", pubkey))
|
||||
}
|
||||
val.Revoked = false
|
||||
k.updateValidator(ctx, val) // update the validator, now unrevoked
|
||||
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
|
||||
return
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// load a delegation
|
||||
func (k Keeper) GetDelegation(ctx sdk.Context,
|
||||
delegatorAddr, validatorAddr sdk.Address) (delegation types.Delegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, validatorAddr, k.cdc))
|
||||
if delegatorBytes == nil {
|
||||
return delegation, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(delegatorBytes, &delegation)
|
||||
return delegation, true
|
||||
}
|
||||
|
||||
// load all delegations used during genesis dump
|
||||
func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, DelegationKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
|
||||
delegations = append(delegations, delegation)
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return delegations
|
||||
}
|
||||
|
||||
// load all delegations for a delegator
|
||||
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address,
|
||||
maxRetrieve int16) (delegations []types.Delegation) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest
|
||||
|
||||
delegations = make([]types.Delegation, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
break
|
||||
}
|
||||
bondBytes := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bondBytes, &delegation)
|
||||
delegations[i] = delegation
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return delegations[:i] // trim
|
||||
}
|
||||
|
||||
// set the delegation
|
||||
func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(delegation)
|
||||
store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc), b)
|
||||
}
|
||||
|
||||
// remove the delegation
|
||||
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// load a unbonding delegation
|
||||
func (k Keeper) GetUnbondingDelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorAddr sdk.Address) (ubd types.UnbondingDelegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
ubdKey := GetUBDKey(DelegatorAddr, ValidatorAddr, k.cdc)
|
||||
bz := store.Get(ubdKey)
|
||||
if bz == nil {
|
||||
return ubd, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(bz, &ubd)
|
||||
return ubd, true
|
||||
}
|
||||
|
||||
// set the unbonding delegation and associated index
|
||||
func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(ubd)
|
||||
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
|
||||
store.Set(ubdKey, bz)
|
||||
store.Set(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc), ubdKey)
|
||||
}
|
||||
|
||||
// remove the unbonding delegation object and associated index
|
||||
func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
ubdKey := GetUBDKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc)
|
||||
store.Delete(ubdKey)
|
||||
store.Delete(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// load a redelegation
|
||||
func (k Keeper) GetRedelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr sdk.Address) (red types.Redelegation, found bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
redKey := GetREDKey(DelegatorAddr, ValidatorSrcAddr, ValidatorDstAddr, k.cdc)
|
||||
bz := store.Get(redKey)
|
||||
if bz == nil {
|
||||
return red, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(bz, &red)
|
||||
return red, true
|
||||
}
|
||||
|
||||
// has a redelegation
|
||||
func (k Keeper) HasReceivingRedelegation(ctx sdk.Context,
|
||||
DelegatorAddr, ValidatorDstAddr sdk.Address) bool {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
prefix := GetREDsByDelToValDstIndexKey(DelegatorAddr, ValidatorDstAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, prefix) //smallest to largest
|
||||
|
||||
found := false
|
||||
if iterator.Valid() {
|
||||
//record found
|
||||
found = true
|
||||
}
|
||||
iterator.Close()
|
||||
return found
|
||||
}
|
||||
|
||||
// set a redelegation and associated index
|
||||
func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(red)
|
||||
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
|
||||
store.Set(redKey, bz)
|
||||
store.Set(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
|
||||
store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc), redKey)
|
||||
}
|
||||
|
||||
// remove a redelegation object and associated index
|
||||
func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
redKey := GetREDKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc)
|
||||
store.Delete(redKey)
|
||||
store.Delete(GetREDByValSrcIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
|
||||
store.Delete(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr, k.cdc))
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// Perform a delegation, set/update everything necessary within the store
|
||||
func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.Address, bondAmt sdk.Coin,
|
||||
validator types.Validator) (newShares sdk.Rat, err sdk.Error) {
|
||||
|
||||
// Get or create the delegator delegation
|
||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
if !found {
|
||||
delegation = types.Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
pool := k.GetPool(ctx)
|
||||
_, _, err = k.coinKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount.Int64())
|
||||
delegation.Shares = delegation.Shares.Add(newShares)
|
||||
|
||||
// Update delegation height
|
||||
delegation.Height = ctx.BlockHeight()
|
||||
|
||||
k.SetPool(ctx, pool)
|
||||
k.SetDelegation(ctx, delegation)
|
||||
k.UpdateValidator(ctx, validator)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// unbond the the delegation return
|
||||
func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address,
|
||||
shares sdk.Rat) (amount int64, err sdk.Error) {
|
||||
|
||||
// check if delegation has any shares in it unbond
|
||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
if !found {
|
||||
err = types.ErrNoDelegatorForAddress(k.Codespace())
|
||||
return
|
||||
}
|
||||
|
||||
// retrieve the amount to remove
|
||||
if delegation.Shares.LT(shares) {
|
||||
err = types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())
|
||||
return
|
||||
}
|
||||
|
||||
// get validator
|
||||
validator, found := k.GetValidator(ctx, validatorAddr)
|
||||
if !found {
|
||||
err = types.ErrNoValidatorFound(k.Codespace())
|
||||
return
|
||||
}
|
||||
|
||||
// subtract shares from delegator
|
||||
delegation.Shares = delegation.Shares.Sub(shares)
|
||||
|
||||
// remove the delegation
|
||||
if delegation.Shares.IsZero() {
|
||||
|
||||
// if the delegation is the owner of the validator then
|
||||
// trigger a revoke validator
|
||||
if bytes.Equal(delegation.DelegatorAddr, validator.Owner) && validator.Revoked == false {
|
||||
validator.Revoked = true
|
||||
}
|
||||
k.RemoveDelegation(ctx, delegation)
|
||||
} else {
|
||||
// Update height
|
||||
delegation.Height = ctx.BlockHeight()
|
||||
k.SetDelegation(ctx, delegation)
|
||||
}
|
||||
|
||||
// remove the coins from the validator
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, amount = validator.RemoveDelShares(pool, shares)
|
||||
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// update then remove validator if necessary
|
||||
validator = k.UpdateValidator(ctx, validator)
|
||||
if validator.DelegatorShares.IsZero() {
|
||||
k.RemoveValidator(ctx, validator.Owner)
|
||||
}
|
||||
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the unbonding delegation
|
||||
params := k.GetParams(ctx)
|
||||
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
MinTime: minTime,
|
||||
Balance: sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)},
|
||||
}
|
||||
k.SetUnbondingDelegation(ctx, ubd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.Address) sdk.Error {
|
||||
|
||||
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
if !found {
|
||||
return types.ErrNoUnbondingDelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// ensure that enough time has passed
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if ubd.MinTime > ctxTime {
|
||||
return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime)
|
||||
}
|
||||
|
||||
k.coinKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{ubd.Balance})
|
||||
k.RemoveUnbondingDelegation(ctx, ubd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, sharesAmount sdk.Rat) sdk.Error {
|
||||
|
||||
// check if this is a transitive redelegation
|
||||
if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) {
|
||||
return types.ErrTransitiveRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delegatorAddr, validatorSrcAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := k.GetParams(ctx)
|
||||
returnCoin := sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)}
|
||||
dstValidator, found := k.GetValidator(ctx, validatorDstAddr)
|
||||
if !found {
|
||||
return types.ErrBadRedelegationDst(k.Codespace())
|
||||
}
|
||||
sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator)
|
||||
|
||||
// create the unbonding delegation
|
||||
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
||||
|
||||
red := types.Redelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
MinTime: minTime,
|
||||
SharesDst: sharesCreated,
|
||||
SharesSrc: sharesAmount,
|
||||
}
|
||||
k.SetRedelegation(ctx, red)
|
||||
return nil
|
||||
}
|
||||
|
||||
// complete unbonding an ongoing redelegation
|
||||
func (k Keeper) CompleteRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.Address) sdk.Error {
|
||||
|
||||
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
||||
if !found {
|
||||
return types.ErrNoRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// ensure that enough time has passed
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if red.MinTime > ctxTime {
|
||||
return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime)
|
||||
}
|
||||
|
||||
k.RemoveRedelegation(ctx, red)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// tests GetDelegation, GetDelegations, SetDelegation, RemoveDelegation, GetDelegations
|
||||
func TestDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 10)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
amts := []int64{9, 8, 7}
|
||||
var validators [3]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
}
|
||||
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
|
||||
// first add a validators[0] to delegate too
|
||||
bond1to1 := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetDelegation(ctx, bond1to1)
|
||||
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.Equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.SetDelegation(ctx, bond1to1)
|
||||
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bond1to1.Equal(resBond))
|
||||
|
||||
// add some more records
|
||||
bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.SetDelegation(ctx, bond1to2)
|
||||
keeper.SetDelegation(ctx, bond1to3)
|
||||
keeper.SetDelegation(ctx, bond2to1)
|
||||
keeper.SetDelegation(ctx, bond2to2)
|
||||
keeper.SetDelegation(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond1to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond1to2.Equal(resBonds[1]))
|
||||
assert.True(t, bond1to3.Equal(resBonds[2]))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bond2to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.Equal(resBonds[1]))
|
||||
assert.True(t, bond2to3.Equal(resBonds[2]))
|
||||
allBonds := keeper.GetAllDelegations(ctx)
|
||||
require.Equal(t, 6, len(allBonds))
|
||||
assert.True(t, bond1to1.Equal(allBonds[0]))
|
||||
assert.True(t, bond1to2.Equal(allBonds[1]))
|
||||
assert.True(t, bond1to3.Equal(allBonds[2]))
|
||||
assert.True(t, bond2to1.Equal(allBonds[3]))
|
||||
assert.True(t, bond2to2.Equal(allBonds[4]))
|
||||
assert.True(t, bond2to3.Equal(allBonds[5]))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveDelegation(ctx, bond2to3)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
assert.True(t, bond2to1.Equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.Equal(resBonds[1]))
|
||||
|
||||
// delete all the records from delegator 2
|
||||
keeper.RemoveDelegation(ctx, bond2to1)
|
||||
keeper.RemoveDelegation(ctx, bond2to2)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
|
||||
assert.False(t, found)
|
||||
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 0, len(resBonds))
|
||||
}
|
||||
|
||||
// tests Get/Set/Remove UnbondingDelegation
|
||||
func TestUnbondingDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 0,
|
||||
MinTime: 0,
|
||||
Balance: sdk.NewCoin("steak", 5),
|
||||
}
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
resBond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, ubd.Equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
ubd.Balance = sdk.NewCoin("steak", 21)
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
resBond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, ubd.Equal(resBond))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveUnbondingDelegation(ctx, ubd)
|
||||
_, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
func TestUnbondDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = 10
|
||||
|
||||
//create a validator and a delegator to that validator
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, 10)
|
||||
require.Equal(t, int64(10), issuedShares.Evaluate())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
require.Equal(t, int64(10), pool.BondedTokens)
|
||||
require.Equal(t, int64(10), validator.PoolShares.Bonded().Evaluate())
|
||||
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
var err error
|
||||
var amount int64
|
||||
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(6), amount) // shares to be added to an unbonding delegation / redelegation
|
||||
|
||||
delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
||||
assert.Equal(t, int64(4), delegation.Shares.Evaluate())
|
||||
assert.Equal(t, int64(4), validator.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(6), pool.LooseTokens, "%v", pool)
|
||||
assert.Equal(t, int64(4), pool.BondedTokens)
|
||||
}
|
||||
|
||||
// tests Get/Set/Remove/Has UnbondingDelegation
|
||||
func TestRedelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 0,
|
||||
MinTime: 0,
|
||||
SharesSrc: sdk.NewRat(5),
|
||||
SharesDst: sdk.NewRat(5),
|
||||
}
|
||||
|
||||
// test shouldn't have and redelegations
|
||||
has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
|
||||
assert.False(t, has)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.True(t, found)
|
||||
assert.True(t, rd.Equal(resBond))
|
||||
|
||||
// check if has the redelegation
|
||||
has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
|
||||
assert.True(t, has)
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
rd.SharesSrc = sdk.NewRat(21)
|
||||
rd.SharesDst = sdk.NewRat(21)
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.True(t, found)
|
||||
assert.True(t, rd.Equal(resBond))
|
||||
|
||||
// delete a record
|
||||
keeper.RemoveRedelegation(ctx, rd)
|
||||
_, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
|
@ -1,35 +1,32 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
const (
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 100000000000 // increased to this precision for accuracy with tests on tick_test.go
|
||||
precision = 100000000000 // increased to this precision for accuracy
|
||||
)
|
||||
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr)
|
||||
|
||||
// process provisions for an hour period
|
||||
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
||||
func (k Keeper) ProcessProvisions(ctx sdk.Context) types.Pool {
|
||||
|
||||
pool := k.GetPool(ctx)
|
||||
pool.Inflation = k.nextInflation(ctx)
|
||||
pool.Inflation = k.NextInflation(ctx)
|
||||
|
||||
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
|
||||
// more bonded tokens are added proportionally to all validators the only term
|
||||
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
|
||||
|
||||
provisions := pool.Inflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat).EvaluateInt()
|
||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate()
|
||||
|
||||
// TODO add to the fees provisions
|
||||
pool.LooseUnbondedTokens = pool.LooseUnbondedTokens.Add(provisions)
|
||||
pool.LooseTokens += provisions
|
||||
return pool
|
||||
}
|
||||
|
||||
// get the next inflation rate for the hour
|
||||
func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
||||
func (k Keeper) NextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
||||
|
||||
params := k.GetParams(ctx)
|
||||
pool := k.GetPool(ctx)
|
||||
|
@ -40,7 +37,7 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
|||
// 7% and 20%.
|
||||
|
||||
// (1 - bondedRatio/GoalBonded) * InflationRateChange
|
||||
inflationRateChangePerYear := sdk.OneRat().Sub(pool.bondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
||||
inflationRateChangePerYear := sdk.OneRat().Sub(pool.BondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
||||
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat)
|
||||
|
||||
// increase the new annual inflation for this next cycle
|
|
@ -1,65 +1,64 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
|
||||
var r = rand.New(rand.NewSource(6595))
|
||||
|
||||
func TestGetInflation(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedTokens / TotalSupply
|
||||
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
zero := sdk.ZeroInt()
|
||||
one := sdk.OneInt()
|
||||
// BondedRatio = BondedTokens / TotalSupply
|
||||
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setBondedTokens, setLooseTokens sdk.Int
|
||||
setBondedTokens, setLooseTokens int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
}{
|
||||
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||
{"test 1", zero, zero, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 100% bonded, starting at 20% inflation and being reduced
|
||||
// (1 - (1/0.67))*(0.13/8667)
|
||||
{"test 2", one, zero, sdk.NewRat(20, 100),
|
||||
{"test 2", 1, 0, sdk.NewRat(20, 100),
|
||||
sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 50% bonded, starting at 10% inflation and being increased
|
||||
{"test 3", one, one, sdk.NewRat(10, 100),
|
||||
{"test 3", 1, 1, sdk.NewRat(10, 100),
|
||||
sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// test 7% minimum stop (testing with 100% bonded)
|
||||
{"test 4", one, zero, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", one, zero, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
{"test 4", 1, 0, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", 1, 0, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
|
||||
// test 20% maximum stop (testing with 0% bonded)
|
||||
{"test 6", zero, zero, sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||
{"test 7", zero, zero, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
|
||||
// perfect balance shouldn't change inflation
|
||||
{"test 8", sdk.NewInt(67), sdk.NewInt(33), sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
{"test 8", 67, 33, sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
pool.BondedTokens, pool.LooseUnbondedTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||
pool.Inflation = tc.setInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
inflation := keeper.nextInflation(ctx)
|
||||
inflation := keeper.NextInflation(ctx)
|
||||
diffInflation := inflation.Sub(tc.setInflation)
|
||||
|
||||
assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
|
@ -69,17 +68,18 @@ func TestGetInflation(t *testing.T) {
|
|||
|
||||
// Test that provisions are correctly added to the pool and validators each hour for 1 year
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 250000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 2
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 250000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 2
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
|
@ -89,7 +89,7 @@ func TestProcessProvisions(t *testing.T) {
|
|||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
_, expProvisions, _ := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
}
|
||||
|
||||
//get the pool and do the final value checks from checkFinalPoolValues
|
||||
|
@ -100,17 +100,18 @@ func TestProcessProvisions(t *testing.T) {
|
|||
// Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate
|
||||
// Cycles through the whole gambit of inflation possibilities, starting at 7% inflation, up to 20%, back down to 7% (it takes ~11.4 years)
|
||||
func TestHourlyInflationRateOfChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 150000000
|
||||
initialUnbondedTokens int64 = 400000000
|
||||
cumulativeExpProvs sdk.Int = sdk.ZeroInt()
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 1
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 150000000
|
||||
initialUnbondedTokens int64 = 400000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
|
@ -121,7 +122,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
|
|||
pool := keeper.GetPool(ctx)
|
||||
previousInflation := pool.Inflation
|
||||
updatedInflation, expProvisions, pool := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
msg := strconv.Itoa(hr)
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
}
|
||||
|
@ -133,7 +134,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) {
|
|||
|
||||
//Test that a large unbonding will significantly lower the bonded ratio
|
||||
func TestLargeUnbond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
|
@ -147,32 +148,33 @@ func TestLargeUnbond(t *testing.T) {
|
|||
validatorTokens = []int64{300000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 7
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[0])
|
||||
validator, found := keeper.GetValidator(ctx, Addrs[0])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
initialBondedRatio := pool.BondedRatio()
|
||||
|
||||
// validator[0] will be unbonded, bringing us from 75% bonded ratio to ~50% (unbonding 300,000,000)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
|
||||
bondedShares = bondedShares.Sub(bondSharesVal0)
|
||||
val0UnbondedTokens = pool.unbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
|
||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.unbondedShareExRate()))
|
||||
val0UnbondedTokens = pool.UnbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
|
||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.UnbondedShareExRate()))
|
||||
|
||||
// unbonded shares should increase
|
||||
assert.True(t, unbondedShares.GT(sdk.NewRat(300000000, 1)))
|
||||
// Ensure that new bonded ratio is less than old bonded ratio , because before they were increasing (i.e. 50% < 75)
|
||||
assert.True(t, (pool.bondedRatio().LT(initialBondedRatio)))
|
||||
assert.True(t, (pool.BondedRatio().LT(initialBondedRatio)))
|
||||
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
@ -181,7 +183,7 @@ func TestLargeUnbond(t *testing.T) {
|
|||
|
||||
//Test that a large bonding will significantly increase the bonded ratio
|
||||
func TestLargeBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
|
@ -193,24 +195,25 @@ func TestLargeBond(t *testing.T) {
|
|||
validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
pool.LooseTokens = initialTotalTokens
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[9])
|
||||
validator, found := keeper.GetValidator(ctx, Addrs[9])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
initialBondedRatio := pool.BondedRatio()
|
||||
|
||||
params := DefaultParams()
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = bondedValidators + 1 //must do this to allow for an extra validator to bond
|
||||
keeper.setParams(ctx, params)
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// validator[9] will be bonded, bringing us from 25% to ~50% (bonding 400,000,000 tokens)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
|
@ -219,7 +222,7 @@ func TestLargeBond(t *testing.T) {
|
|||
// unbonded shares should decrease
|
||||
assert.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1)))
|
||||
// Ensure that new bonded ratio is greater than old bonded ratio (i.e. 50% > 25%)
|
||||
assert.True(t, (pool.bondedRatio().GT(initialBondedRatio)))
|
||||
assert.True(t, (pool.BondedRatio().GT(initialBondedRatio)))
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
||||
|
@ -228,20 +231,19 @@ func TestLargeBond(t *testing.T) {
|
|||
|
||||
// Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators
|
||||
func TestInflationWithRandomOperations(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.ZeroInt())
|
||||
params := DefaultParams()
|
||||
keeper.setParams(ctx, params)
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
params := types.DefaultParams()
|
||||
keeper.SetParams(ctx, params)
|
||||
numValidators := 20
|
||||
|
||||
// start off by randomly setting up 20 validators
|
||||
pool, validators := randomSetup(r, numValidators)
|
||||
pool, validators := types.RandomSetup(r, numValidators)
|
||||
require.Equal(t, numValidators, len(validators))
|
||||
|
||||
for i := 0; i < len(validators); i++ {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
keeper.SetValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// Used to rotate validators so each random operation is applied to a different validator
|
||||
validatorCounter := 0
|
||||
|
@ -250,30 +252,30 @@ func TestInflationWithRandomOperations(t *testing.T) {
|
|||
for i := 0; i < numValidators; i++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// Get inflation before randomOperation, for comparison later
|
||||
// Get inflation before RandomOperation, for comparison later
|
||||
previousInflation := pool.Inflation
|
||||
|
||||
// Perform the random operation, and record how validators are modified
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, pool, validators[validatorCounter])
|
||||
validatorsMod := make([]Validator, len(validators))
|
||||
poolMod, validatorMod, tokens, msg := types.RandomOperation(r)(r, pool, validators[validatorCounter])
|
||||
validatorsMod := make([]types.Validator, len(validators))
|
||||
copy(validatorsMod[:], validators[:])
|
||||
require.Equal(t, numValidators, len(validators), "i %v", validatorCounter)
|
||||
require.Equal(t, numValidators, len(validatorsMod), "i %v", validatorCounter)
|
||||
validatorsMod[validatorCounter] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
types.AssertInvariants(t, msg,
|
||||
pool, validators,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
// set pool and validators after the random operation
|
||||
pool = poolMod
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators = validatorsMod
|
||||
|
||||
// Must set inflation here manually, as opposed to most other tests in this suite, where we call keeper.processProvisions(), which updates pool.Inflation
|
||||
updatedInflation := keeper.nextInflation(ctx)
|
||||
updatedInflation := keeper.NextInflation(ctx)
|
||||
pool.Inflation = updatedInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// Ensure inflation changes as expected when random operations are applied.
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
|
@ -285,41 +287,43 @@ func TestInflationWithRandomOperations(t *testing.T) {
|
|||
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
|
||||
|
||||
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
|
||||
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens int64, cumulativeExpProvs sdk.Int) {
|
||||
calculatedTotalTokens := cumulativeExpProvs.AddRaw(initialTotalTokens)
|
||||
|
||||
assert.Equal(t, calculatedTotalTokens.Int64(), pool.TokenSupply().Int64())
|
||||
func checkFinalPoolValues(t *testing.T, pool types.Pool, initialTotalTokens, cumulativeExpProvs int64) {
|
||||
calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs
|
||||
assert.Equal(t, calculatedTotalTokens, pool.TokenSupply())
|
||||
}
|
||||
|
||||
// Processes provisions are added to the pool correctly every hour
|
||||
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
|
||||
func updateProvisions(t *testing.T, keeper Keeper, pool Pool, ctx sdk.Context, hr int) (sdk.Rat, sdk.Int, Pool) {
|
||||
expInflation := keeper.nextInflation(ctx)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRatFromInt(pool.TokenSupply())).Quo(hrsPerYrRat)).EvaluateInt()
|
||||
func updateProvisions(t *testing.T, keeper Keeper, pool types.Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, types.Pool) {
|
||||
expInflation := keeper.NextInflation(ctx)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate()
|
||||
startTotalSupply := pool.TokenSupply()
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
pool = keeper.ProcessProvisions(ctx)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
//check provisions were added to pool
|
||||
require.Equal(t, startTotalSupply.Add(expProvisions).Int64(), pool.TokenSupply().Int64())
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
|
||||
|
||||
return expInflation, expProvisions, pool
|
||||
}
|
||||
|
||||
// Deterministic setup of validators and pool
|
||||
// Allows you to decide how many validators to setup
|
||||
// Allows you to pick which validators are bonded by adjusting the MaxValidators of params
|
||||
func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64, maxValidators uint16) ([]Validator, Keeper, Pool) {
|
||||
params := DefaultParams()
|
||||
func setupTestValidators(pool types.Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64,
|
||||
maxValidators uint16) ([]types.Validator, Keeper, types.Pool) {
|
||||
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = maxValidators
|
||||
keeper.setParams(ctx, params)
|
||||
keeper.SetParams(ctx, params)
|
||||
numValidators := len(validatorTokens)
|
||||
validators := make([]Validator, numValidators)
|
||||
validators := make([]types.Validator, numValidators)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i], pool, _ = validators[i].addTokensFromDel(pool, sdk.NewInt(validatorTokens[i]))
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[i] = keeper.updateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, validatorTokens[i])
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[i] = keeper.UpdateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
||||
pool = keeper.GetPool(ctx)
|
||||
}
|
||||
|
||||
|
@ -327,28 +331,28 @@ func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTok
|
|||
}
|
||||
|
||||
// Checks that the deterministic validator setup you wanted matches the values in the pool
|
||||
func checkValidatorSetup(t *testing.T, pool Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
||||
assert.Equal(t, initialTotalTokens, pool.TokenSupply().Int64())
|
||||
assert.Equal(t, initialBondedTokens, pool.BondedTokens.Int64())
|
||||
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens.Int64())
|
||||
func checkValidatorSetup(t *testing.T, pool types.Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
||||
assert.Equal(t, initialTotalTokens, pool.TokenSupply(), "%v", pool)
|
||||
assert.Equal(t, initialBondedTokens, pool.BondedTokens, "%v", pool)
|
||||
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens, "%v", pool)
|
||||
|
||||
// test initial bonded ratio
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.bondedRatio())
|
||||
assert.True(t, pool.BondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.BondedRatio())
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
|
||||
assert.True(t, pool.BondedShareExRate().Equal(sdk.OneRat()), "%v", pool.BondedShareExRate())
|
||||
}
|
||||
|
||||
// Checks that The inflation will correctly increase or decrease after an update to the pool
|
||||
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||
func checkInflation(t *testing.T, pool types.Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||
inflationChange := updatedInflation.Sub(previousInflation)
|
||||
|
||||
switch {
|
||||
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||
assert.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||
|
||||
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(20, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
||||
|
@ -358,11 +362,11 @@ func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation
|
|||
}
|
||||
|
||||
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||
assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||
|
||||
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(7, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// keeper of the stake store
|
||||
type Keeper struct {
|
||||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.Keeper
|
||||
|
||||
// codespace
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
keeper := Keeper{
|
||||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
codespace: codespace,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// return the codespace
|
||||
func (k Keeper) Codespace() sdk.CodespaceType {
|
||||
return k.codespace
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// some generic reads/writes that don't need their own files
|
||||
|
||||
// load/save the global staking params
|
||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
b := store.Get(ParamKey)
|
||||
if b == nil {
|
||||
panic("Stored params should not have been nil")
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinary(b, ¶ms)
|
||||
return
|
||||
}
|
||||
|
||||
// Need a distinct function because setParams depends on an existing previous
|
||||
// record of params to exist (to check if maxValidators has changed) - and we
|
||||
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
|
||||
// first params set it will panic.
|
||||
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
// set the params
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
exParams := k.GetParams(ctx)
|
||||
|
||||
// if max validator count changes, must recalculate validator set
|
||||
if exParams.MaxValidators != params.MaxValidators {
|
||||
k.UpdateBondedValidatorsFull(ctx)
|
||||
}
|
||||
b := k.cdc.MustMarshalBinary(params)
|
||||
store.Set(ParamKey, b)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// load/save the pool
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(PoolKey)
|
||||
if b == nil {
|
||||
panic("Stored pool should not have been nil")
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &pool)
|
||||
return
|
||||
}
|
||||
|
||||
// set the pool
|
||||
func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := k.cdc.MustMarshalBinary(pool)
|
||||
store.Set(PoolKey, b)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) InitIntraTxCounter(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
if b == nil {
|
||||
k.SetIntraTxCounter(ctx, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// get the current in-block validator operation counter
|
||||
func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(IntraTxCounterKey)
|
||||
var counter int16
|
||||
k.cdc.MustUnmarshalBinary(b, &counter)
|
||||
return counter
|
||||
}
|
||||
|
||||
// set the current in-block validator operation counter
|
||||
func (k Keeper) SetIntraTxCounter(ctx sdk.Context, counter int16) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(counter)
|
||||
store.Set(IntraTxCounterKey, bz)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
expParams := types.DefaultParams()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resParams := keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.Equal(resParams))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expParams.MaxValidators = 777
|
||||
keeper.SetParams(ctx, expParams)
|
||||
resParams = keeper.GetParams(ctx)
|
||||
assert.True(t, expParams.Equal(resParams))
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
expPool := types.InitialPool()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.Equal(resPool))
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.BondedTokens = 777
|
||||
keeper.SetPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.True(t, expPool.Equal(resPool))
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// TODO remove some of these prefixes once have working multistore
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
ParamKey = []byte{0x00} // key for parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for the staking pools
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
|
||||
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
|
||||
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
|
||||
ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator
|
||||
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
|
||||
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
|
||||
IntraTxCounterKey = []byte{0x09} // key for intra-block tx index
|
||||
DelegationKey = []byte{0x0A} // key for a delegation
|
||||
UnbondingDelegationKey = []byte{0x0B} // key for an unbonding-delegation
|
||||
UnbondingDelegationByValIndexKey = []byte{0x0C} // prefix for each key for an unbonding-delegation, by validator owner
|
||||
RedelegationKey = []byte{0x0D} // key for a redelegation
|
||||
RedelegationByValSrcIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by validator owner
|
||||
RedelegationByValDstIndexKey = []byte{0x0F} // prefix for each key for an redelegation, by validator owner
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
||||
// get the key for the validator with address
|
||||
func GetValidatorKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator with pubkey
|
||||
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
|
||||
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the current validator group, ordered like tendermint
|
||||
func GetValidatorsBondedIndexKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsBondedIndexKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the power which is the key for the validator used in the power-store
|
||||
func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte {
|
||||
|
||||
// NOTE the address doesn't need to be stored because counter bytes must always be different
|
||||
return GetValidatorPowerRank(validator, pool)
|
||||
}
|
||||
|
||||
// get the power of a validator
|
||||
func GetValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
|
||||
|
||||
power := validator.EquivalentBondedShares(pool)
|
||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
revokedBytes := make([]byte, 1)
|
||||
if validator.Revoked {
|
||||
revokedBytes[0] = byte(0x01)
|
||||
} else {
|
||||
revokedBytes[0] = byte(0x00)
|
||||
}
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
|
||||
return append(ValidatorsByPowerIndexKey,
|
||||
append(revokedBytes,
|
||||
append(powerBytes,
|
||||
append(heightBytes, counterBytes...)...)...)...)
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
|
||||
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for delegator bond with validator
|
||||
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the prefix for a delegator for all validators
|
||||
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(DelegationKey, res...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for an unbonding delegation
|
||||
func GetUBDKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetUBDsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the index-key for an unbonding delegation, stored by validator-index
|
||||
func GetUBDByValIndexKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetUBDsByValIndexKey(validatorAddr, cdc), delegatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
//______________
|
||||
|
||||
// get the prefix for all unbonding delegations from a delegator
|
||||
func GetUBDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(UnbondingDelegationKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for the indexs of unbonding delegations for a validator
|
||||
func GetUBDsByValIndexKey(validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorAddr)
|
||||
return append(UnbondingDelegationByValIndexKey, res...)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
|
||||
// get the key for a redelegation
|
||||
func GetREDKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsKey(delegatorAddr, cdc),
|
||||
append(
|
||||
validatorSrcAddr.Bytes(),
|
||||
validatorDstAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
// get the index-key for a redelegation, stored by source-validator-index
|
||||
func GetREDByValSrcIndexKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsFromValSrcIndexKey(validatorSrcAddr, cdc),
|
||||
append(
|
||||
delegatorAddr.Bytes(),
|
||||
validatorDstAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
// get the index-key for a redelegation, stored by destination-validator-index
|
||||
func GetREDByValDstIndexKey(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
|
||||
append(
|
||||
delegatorAddr.Bytes(),
|
||||
validatorSrcAddr.Bytes()...)...,
|
||||
)
|
||||
}
|
||||
|
||||
//______________
|
||||
|
||||
// get the prefix keyspace for redelegations from a delegator
|
||||
func GetREDsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&delegatorAddr)
|
||||
return append(RedelegationKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating away from a source validator
|
||||
func GetREDsFromValSrcIndexKey(validatorSrcAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorSrcAddr)
|
||||
return append(RedelegationByValSrcIndexKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating towards a destination validator
|
||||
func GetREDsToValDstIndexKey(validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res := cdc.MustMarshalBinary(&validatorDstAddr)
|
||||
return append(RedelegationByValDstIndexKey, res...)
|
||||
}
|
||||
|
||||
// get the prefix keyspace for all redelegations redelegating towards a destination validator
|
||||
// from a particular delegator
|
||||
func GetREDsByDelToValDstIndexKey(delegatorAddr sdk.Address,
|
||||
validatorDstAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
|
||||
return append(
|
||||
GetREDsToValDstIndexKey(validatorDstAddr, cdc),
|
||||
delegatorAddr.Bytes()...)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// Implements ValidatorSet
|
||||
var _ sdk.ValidatorSet = Keeper{}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
// get the sdk.validator for a particular address
|
||||
func (k Keeper) Validator(ctx sdk.Context, address sdk.Address) sdk.Validator {
|
||||
val, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// total power from the bond
|
||||
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
||||
pool := k.GetPool(ctx)
|
||||
return pool.BondedShares
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// Implements DelegationSet
|
||||
|
||||
var _ sdk.DelegationSet = Keeper{}
|
||||
|
||||
// Returns self as it is both a validatorset and delegationset
|
||||
func (k Keeper) GetValidatorSet() sdk.ValidatorSet {
|
||||
return k
|
||||
}
|
||||
|
||||
// get the delegation for a particular set of delegator and validator addresses
|
||||
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
|
||||
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return bond
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.Address, fn func(index int64, delegation sdk.Delegation) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := GetDelegationsKey(delAddr, k.cdc)
|
||||
iterator := sdk.KVStorePrefixIterator(store, key)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
bz := iterator.Value()
|
||||
var delegation types.Delegation
|
||||
k.cdc.MustUnmarshalBinary(bz, &delegation)
|
||||
stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// NOTE the current slash functionality doesn't take into consideration unbonding/rebonding records
|
||||
// or the time of breach. This will be updated in slashing v2
|
||||
// slash a validator
|
||||
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
|
||||
|
||||
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Attempted to slash a nonexistent validator with address %s", pubkey.Address()))
|
||||
}
|
||||
sharesToRemove := validator.PoolShares.Amount.Mul(fraction)
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, burned := validator.RemovePoolShares(pool, sharesToRemove)
|
||||
k.SetPool(ctx, pool) // update the pool
|
||||
k.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %d tokens", pubkey.Address(), fraction, sharesToRemove, burned))
|
||||
return
|
||||
}
|
||||
|
||||
// revoke a validator
|
||||
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot revoke", pubkey))
|
||||
}
|
||||
validator.Revoked = true
|
||||
k.UpdateValidator(ctx, validator) // update the validator, now revoked
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// unrevoke a validator
|
||||
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot unrevoke", pubkey))
|
||||
}
|
||||
validator.Revoked = false
|
||||
k.UpdateValidator(ctx, validator) // update the validator, now unrevoked
|
||||
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package stake
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -18,35 +18,52 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// dummy addresses used for testing
|
||||
var (
|
||||
addrs = createTestAddrs(100)
|
||||
pks = createTestPubKeys(100)
|
||||
Addrs = createTestAddrs(100)
|
||||
PKs = createTestPubKeys(100)
|
||||
emptyAddr sdk.Address
|
||||
emptyPubkey crypto.PubKey
|
||||
|
||||
addrDels = []sdk.Address{
|
||||
Addrs[0],
|
||||
Addrs[1],
|
||||
}
|
||||
addrVals = []sdk.Address{
|
||||
Addrs[2],
|
||||
Addrs[3],
|
||||
Addrs[4],
|
||||
Addrs[5],
|
||||
Addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
// intended to be used with require/assert: require.True(ValEq(...))
|
||||
func ValEq(t *testing.T, exp, got Validator) (*testing.T, bool, string, Validator, Validator) {
|
||||
return t, exp.equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) {
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
func makeTestCodec() *wire.Codec {
|
||||
// create a codec used only for testing
|
||||
func MakeTestCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil)
|
||||
cdc.RegisterConcrete(MsgCreateValidator{}, "test/stake/CreateValidator", nil)
|
||||
cdc.RegisterConcrete(MsgEditValidator{}, "test/stake/EditValidator", nil)
|
||||
cdc.RegisterConcrete(MsgUnbond{}, "test/stake/Unbond", nil)
|
||||
cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil)
|
||||
cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil)
|
||||
cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil)
|
||||
cdc.RegisterConcrete(types.MsgCompleteUnbonding{}, "test/stake/CompleteUnbonding", nil)
|
||||
cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/stake/BeginRedelegate", nil)
|
||||
cdc.RegisterConcrete(types.MsgCompleteRedelegate{}, "test/stake/CompleteRedelegate", nil)
|
||||
|
||||
// Register AppAccount
|
||||
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
||||
|
@ -56,8 +73,9 @@ func makeTestCodec() *wire.Codec {
|
|||
return cdc
|
||||
}
|
||||
|
||||
func paramsNoInflation() Params {
|
||||
return Params{
|
||||
// default params without inflation
|
||||
func ParamsNoInflation() types.Params {
|
||||
return types.Params{
|
||||
InflationRateChange: sdk.ZeroRat(),
|
||||
InflationMax: sdk.ZeroRat(),
|
||||
InflationMin: sdk.ZeroRat(),
|
||||
|
@ -68,7 +86,7 @@ func paramsNoInflation() Params {
|
|||
}
|
||||
|
||||
// hogpodge of all sorts of input required for testing
|
||||
func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Context, auth.AccountMapper, Keeper) {
|
||||
func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) {
|
||||
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
keyAcc := sdk.NewKVStoreKey("acc")
|
||||
|
@ -81,28 +99,32 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins sdk.Int) (sdk.Conte
|
|||
require.Nil(t, err)
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
|
||||
cdc := makeTestCodec()
|
||||
cdc := MakeTestCodec()
|
||||
accountMapper := auth.NewAccountMapper(
|
||||
cdc, // amino codec
|
||||
keyAcc, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
ck := bank.NewKeeper(accountMapper)
|
||||
keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace)
|
||||
keeper.setPool(ctx, InitialPool())
|
||||
keeper.setNewParams(ctx, DefaultParams())
|
||||
keeper := NewKeeper(cdc, keyStake, ck, types.DefaultCodespace)
|
||||
keeper.SetPool(ctx, types.InitialPool())
|
||||
keeper.SetNewParams(ctx, types.DefaultParams())
|
||||
keeper.InitIntraTxCounter(ctx)
|
||||
|
||||
// fill all the addresses with some coins
|
||||
for _, addr := range addrs {
|
||||
// fill all the addresses with some coins, set the loose pool tokens simultaneously
|
||||
for _, addr := range Addrs {
|
||||
pool := keeper.GetPool(ctx)
|
||||
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
{keeper.GetParams(ctx).BondDenom, initCoins},
|
||||
{keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
|
||||
})
|
||||
pool.LooseTokens += initCoins
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
return ctx, accountMapper, keeper
|
||||
}
|
||||
|
||||
func newPubKey(pk string) (res crypto.PubKey) {
|
||||
func NewPubKey(pk string) (res crypto.PubKey) {
|
||||
pkBytes, err := hex.DecodeString(pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -114,7 +136,7 @@ func newPubKey(pk string) (res crypto.PubKey) {
|
|||
}
|
||||
|
||||
// for incode address generation
|
||||
func testAddr(addr string, bech string) sdk.Address {
|
||||
func TestAddr(addr string, bech string) sdk.Address {
|
||||
|
||||
res, err := sdk.GetAccAddressHex(addr)
|
||||
if err != nil {
|
||||
|
@ -151,7 +173,7 @@ func createTestAddrs(numAddrs int) []sdk.Address {
|
|||
buffer.WriteString(numString) //adding on final two digits to make addresses unique
|
||||
res, _ := sdk.GetAccAddressHex(buffer.String())
|
||||
bech, _ := sdk.Bech32ifyAcc(res)
|
||||
addresses = append(addresses, testAddr(buffer.String(), bech))
|
||||
addresses = append(addresses, TestAddr(buffer.String(), bech))
|
||||
buffer.Reset()
|
||||
}
|
||||
return addresses
|
||||
|
@ -166,8 +188,16 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
|
|||
numString := strconv.Itoa(i)
|
||||
buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string
|
||||
buffer.WriteString(numString) //adding on final two digits to make pubkeys unique
|
||||
publicKeys = append(publicKeys, newPubKey(buffer.String()))
|
||||
publicKeys = append(publicKeys, NewPubKey(buffer.String()))
|
||||
buffer.Reset()
|
||||
}
|
||||
return publicKeys
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// does a certain by-power index record exist
|
||||
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
|
@ -0,0 +1,537 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// get a single validator
|
||||
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator types.Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(GetValidatorKey(addr))
|
||||
if b == nil {
|
||||
return validator, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinary(b, &validator)
|
||||
return validator, true
|
||||
}
|
||||
|
||||
// get a single validator by pubkey
|
||||
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator types.Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
|
||||
if addr == nil {
|
||||
return validator, false
|
||||
}
|
||||
return k.GetValidator(ctx, addr)
|
||||
}
|
||||
|
||||
// set the main record holding validator details
|
||||
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set main store
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bz)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorByPubKeyIndex(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// set pointer by pubkey
|
||||
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.Owner)
|
||||
}
|
||||
|
||||
// validator index
|
||||
func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
|
||||
}
|
||||
|
||||
// used in testing
|
||||
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
||||
|
||||
// Get the set of all validators with no limits, used during genesis dump
|
||||
func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() {
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators = append(validators, validator)
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators
|
||||
}
|
||||
|
||||
// Get the set of all validators, retrieve a maxRetrieve number of records
|
||||
func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
|
||||
|
||||
validators = make([]types.Validator, maxRetrieve)
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if !iterator.Valid() || i > int(maxRetrieve-1) {
|
||||
break
|
||||
}
|
||||
bz := iterator.Value()
|
||||
var validator types.Validator
|
||||
k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
validators[i] = validator
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// get the group of the bonded validators
|
||||
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators = make([]types.Validator, maxValidators)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
i := 0
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
// sanity check
|
||||
if i > int(maxValidators-1) {
|
||||
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// get the group of bonded validators sorted by power-rank
|
||||
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
validators := make([]types.Validator, maxValidators)
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
i := 0
|
||||
for {
|
||||
if !iterator.Valid() || i > int(maxValidators-1) {
|
||||
break
|
||||
}
|
||||
address := iterator.Value()
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
if validator.Status() == sdk.Bonded {
|
||||
validators[i] = validator
|
||||
i++
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
// Accumulated updates to the active/bonded validator set for tendermint
|
||||
|
||||
// get the most recently updated validators
|
||||
func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val abci.Validator
|
||||
k.cdc.MustUnmarshalBinary(valBytes, &val)
|
||||
updates = append(updates, val)
|
||||
}
|
||||
iterator.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remove all validator update entries after applied to Tendermint
|
||||
func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// delete subspace
|
||||
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
iterator.Close()
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// perfom all the nessisary steps for when a validator changes its power
|
||||
// updates all validator stores as well as tendermint update store
|
||||
// may kick out validators if new validator is entering the bonded validator group
|
||||
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
ownerAddr := validator.Owner
|
||||
|
||||
// always update the main list ordered by owner address before exiting
|
||||
defer func() {
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(ownerAddr), bz)
|
||||
}()
|
||||
|
||||
// retrieve the old validator record
|
||||
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
|
||||
|
||||
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
validator = k.unbondValidator(ctx, validator)
|
||||
|
||||
// need to also clear the cliff validator spot because the revoke has
|
||||
// opened up a new spot which will be filled when
|
||||
// updateValidatorsBonded is called
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
|
||||
powerIncreasing := false
|
||||
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
|
||||
powerIncreasing = true
|
||||
}
|
||||
|
||||
// if already a validator, copy the old block height and counter, else set them
|
||||
if oldFound && oldValidator.Status() == sdk.Bonded {
|
||||
validator.BondHeight = oldValidator.BondHeight
|
||||
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
|
||||
} else {
|
||||
validator.BondHeight = ctx.BlockHeight()
|
||||
counter := k.GetIntraTxCounter(ctx)
|
||||
validator.BondIntraTxCounter = counter
|
||||
k.SetIntraTxCounter(ctx, counter+1)
|
||||
}
|
||||
|
||||
// update the list ordered by voting power
|
||||
if oldFound {
|
||||
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
|
||||
}
|
||||
valPower := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
store.Set(valPower, validator.Owner)
|
||||
|
||||
// efficiency case:
|
||||
// if already bonded and power increasing only need to update tendermint
|
||||
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
|
||||
return validator
|
||||
}
|
||||
|
||||
// efficiency case:
|
||||
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
|
||||
cliffPower := k.getCliffValidatorPower(ctx)
|
||||
if cliffPower != nil &&
|
||||
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
|
||||
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
|
||||
return validator
|
||||
}
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal := k.UpdateBondedValidators(ctx, validator)
|
||||
if updatedVal.Owner != nil { // updates to validator occurred to be updated
|
||||
validator = updatedVal
|
||||
}
|
||||
return validator
|
||||
}
|
||||
|
||||
// Update the validator group and kick out any old validators. In addition this
|
||||
// function adds (or doesn't add) a validator which has updated its bonded
|
||||
// tokens to the validator group. -> this validator is specified through the
|
||||
// updatedValidatorAddr term.
|
||||
//
|
||||
// The correct subset is retrieved by iterating through an index of the
|
||||
// validators sorted by power, stored using the ValidatorsByPowerIndexKey.
|
||||
// Simultaneously the current validator records are updated in store with the
|
||||
// ValidatorsBondedIndexKey. This store is used to determine if a validator is a
|
||||
// validator without needing to iterate over the subspace as we do in
|
||||
// GetValidators.
|
||||
//
|
||||
// Optionally also return the validator from a retrieve address if the validator has been bonded
|
||||
func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
|
||||
newValidator types.Validator) (updatedVal types.Validator) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
kickCliffValidator := false
|
||||
oldCliffValidatorAddr := k.getCliffValidator(ctx)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator types.Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
// TODO benchmark if we should read the current power and not write if it's the same
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store, or under the
|
||||
// situation that this is the "new validator" just use the validator
|
||||
// provided because it has not yet been updated in the main validator
|
||||
// store
|
||||
ownerAddr := iterator.Value()
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
validator = newValidator
|
||||
} else {
|
||||
var found bool
|
||||
validator, found = k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
}
|
||||
|
||||
// if not previously a validator (and unrevoked),
|
||||
// kick the cliff validator / bond this new validator
|
||||
if validator.Status() != sdk.Bonded && !validator.Revoked {
|
||||
kickCliffValidator = true
|
||||
|
||||
validator = k.bondValidator(ctx, validator)
|
||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||
updatedVal = validator
|
||||
}
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// perform the actual kicks
|
||||
if oldCliffValidatorAddr != nil && kickCliffValidator {
|
||||
validator, found := k.GetValidator(ctx, oldCliffValidatorAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// full update of the bonded validator set, many can be added/kicked
|
||||
func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// clear the current validators store, add to the ToKickOut temp store
|
||||
toKickOut := make(map[string]byte)
|
||||
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
ownerAddr := iterator.Value()
|
||||
toKickOut[string(ownerAddr)] = 0 // set anything
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator types.Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store,
|
||||
// or under the situation that this is the "new validator" just
|
||||
// use the validator provided because it has not yet been updated
|
||||
// in the main validator store
|
||||
ownerAddr := iterator.Value()
|
||||
var found bool
|
||||
validator, found = k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
|
||||
_, found = toKickOut[string(ownerAddr)]
|
||||
if found {
|
||||
delete(toKickOut, string(ownerAddr))
|
||||
} else {
|
||||
|
||||
// if it wasn't in the toKickOut group it means
|
||||
// this wasn't a previously a validator, therefor
|
||||
// update the validator to enter the validator group
|
||||
validator = k.bondValidator(ctx, validator)
|
||||
}
|
||||
|
||||
if validator.Revoked && validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
} else {
|
||||
bondedValidatorsCount++
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// perform the actual kicks
|
||||
for key := range toKickOut {
|
||||
ownerAddr := []byte(key)
|
||||
validator, found := k.GetValidator(ctx, ownerAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes unbonded
|
||||
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Unbonded {
|
||||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// save the now unbonded validator record
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
// also remove from the Bonded types.Validators Store
|
||||
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
|
||||
return validator
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes bonded
|
||||
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// save the now bonded validator record to the three referenced stores
|
||||
bzVal := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorKey(validator.Owner), bzVal)
|
||||
store.Set(GetValidatorsBondedIndexKey(validator.Owner), validator.Owner)
|
||||
|
||||
// add to accumulated changes for tendermint
|
||||
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bzABCI)
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
// remove the validator record and associated indexes
|
||||
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.Address) {
|
||||
|
||||
// first retrieve the old validator record
|
||||
validator, found := k.GetValidator(ctx, address)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
// delete the old validator record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
store.Delete(GetValidatorKey(address))
|
||||
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
|
||||
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
|
||||
|
||||
// delete from the current and power weighted validator groups if the validator
|
||||
// is bonded - and add validator with zero power to the validator updates
|
||||
if store.Get(GetValidatorsBondedIndexKey(validator.Owner)) == nil {
|
||||
return
|
||||
}
|
||||
store.Delete(GetValidatorsBondedIndexKey(validator.Owner))
|
||||
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero(k.cdc))
|
||||
store.Set(GetTendermintUpdatesKey(address), bz)
|
||||
}
|
||||
|
||||
//__________________________________________________________________________
|
||||
|
||||
// get the current validator on the cliff
|
||||
func (k Keeper) getCliffValidator(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorCliffIndexKey)
|
||||
}
|
||||
|
||||
// get the current power of the validator on the cliff
|
||||
func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(ValidatorPowerCliffKey)
|
||||
}
|
||||
|
||||
// set the current validator and power of the validator on the cliff
|
||||
func (k Keeper) setCliffValidator(ctx sdk.Context, validator types.Validator, pool types.Pool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
store.Set(ValidatorPowerCliffKey, bz)
|
||||
store.Set(ValidatorCliffIndexKey, validator.Owner)
|
||||
}
|
||||
|
||||
// clear the current validator and power of the validator on the cliff
|
||||
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(ValidatorPowerCliffKey)
|
||||
store.Delete(ValidatorCliffIndexKey)
|
||||
}
|
|
@ -0,0 +1,674 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 10)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// test how the validator is set from a purely unbonbed pool
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, _ = validator.AddTokensFromDel(pool, 10)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validator)
|
||||
|
||||
// after the save the validator should be bonded
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
|
||||
// Check each store for being saved
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.True(ValEq(t, validator, resVal))
|
||||
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
resVals = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validator.ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create a random pool
|
||||
pool.LooseTokens = 10000
|
||||
pool.BondedTokens = 1234
|
||||
pool.BondedShares = sdk.NewRat(124)
|
||||
pool.UnbondingTokens = 13934
|
||||
pool.UnbondingShares = sdk.NewRat(145)
|
||||
pool.UnbondedTokens = 154
|
||||
pool.UnbondedShares = sdk.NewRat(1333)
|
||||
keeper.SetPool(ctx, pool)
|
||||
|
||||
// add a validator
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, delSharesCreated := validator.AddTokensFromDel(pool, 100)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validator)
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// burn half the delegator shares
|
||||
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||
assert.Equal(t, int64(50), burned)
|
||||
keeper.SetPool(ctx, pool) // update the pool
|
||||
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
power = GetValidatorsByPowerIndexKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
}
|
||||
|
||||
// This function tests UpdateValidator, GetValidator, GetValidatorsBonded, RemoveValidator
|
||||
func TestValidatorBasics(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
var validators [3]types.Validator
|
||||
amts := []int64{9, 8, 7}
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||
validators[i].PoolShares = types.NewUnbondedShares(sdk.ZeroRat())
|
||||
validators[i].AddTokensFromDel(pool, amt)
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
assert.Zero(t, len(resVals))
|
||||
|
||||
// set and retrieve a record
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(10))
|
||||
validators[0].DelegatorShares = sdk.NewRat(10)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// add other validators
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[1], resVal))
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[2])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[2], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 3, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here
|
||||
assert.True(ValEq(t, validators[1], resVals[1]))
|
||||
assert.True(ValEq(t, validators[2], resVals[2]))
|
||||
|
||||
// remove a record
|
||||
keeper.RemoveValidator(ctx, validators[1].Owner)
|
||||
_, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetValidatorsByPower
|
||||
func GetValidatorSortingUnmixed(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i].PoolShares = types.NewBondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
|
||||
// test a basic increase in voting power
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(500))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(200))
|
||||
ctx = ctx.WithBlockHeight(10)
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
|
||||
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
|
||||
|
||||
// no change in voting power - no change in sort
|
||||
ctx = ctx.WithBlockHeight(20)
|
||||
keeper.UpdateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
||||
keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
ctx = ctx.WithBlockHeight(30)
|
||||
keeper.UpdateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n, "%v", resValidators)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
}
|
||||
|
||||
func GetValidatorSortingMixed(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
|
||||
n := len(amts)
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[0]))
|
||||
validators[1].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[1]))
|
||||
validators[2].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[2]))
|
||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(amts[3]))
|
||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(amts[4]))
|
||||
for i := range amts {
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
val0, found := keeper.GetValidator(ctx, Addrs[0])
|
||||
require.True(t, found)
|
||||
val1, found := keeper.GetValidator(ctx, Addrs[1])
|
||||
require.True(t, found)
|
||||
val2, found := keeper.GetValidator(ctx, Addrs[2])
|
||||
require.True(t, found)
|
||||
val3, found := keeper.GetValidator(ctx, Addrs[3])
|
||||
require.True(t, found)
|
||||
val4, found := keeper.GetValidator(ctx, Addrs[4])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, sdk.Unbonded, val0.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val1.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val2.Status())
|
||||
assert.Equal(t, sdk.Bonded, val3.Status())
|
||||
assert.Equal(t, sdk.Bonded, val4.Status())
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
}
|
||||
|
||||
// TODO separate out into multiple tests
|
||||
func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
var found bool
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
nMax := uint16(2)
|
||||
params.MaxValidators = nMax
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400}
|
||||
var validators [4]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[i] = keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 500)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// A validator which leaves the gotValidator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
|
||||
// validator 3 enters bonded validator set
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
|
||||
validators[3], found = keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.True(t, found)
|
||||
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 1)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// validator 3 kicked out temporarily
|
||||
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewRat(201))
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// validator 4 does not get spot back
|
||||
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, 200)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
validator, exists := keeper.GetValidator(ctx, validators[3].Owner)
|
||||
require.Equal(t, exists, true)
|
||||
require.Equal(t, int64(40), validator.BondHeight)
|
||||
}
|
||||
|
||||
func TestValidatorBondHeight(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
pool := keeper.GetPool(ctx)
|
||||
var validators [3]types.Validator
|
||||
validators[0] = types.NewValidator(Addrs[0], PKs[0], types.Description{})
|
||||
validators[1] = types.NewValidator(Addrs[1], PKs[1], types.Description{})
|
||||
validators[2] = types.NewValidator(Addrs[2], PKs[2], types.Description{})
|
||||
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 200)
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 100)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 100)
|
||||
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
////////////////////////////////////////
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should become bonded
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
|
||||
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[1], resValidators[1]))
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 50)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 50)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestFullValidatorSetPowerChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
params := keeper.GetParams(ctx)
|
||||
max := 2
|
||||
params.MaxValidators = uint16(2)
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400, 200}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
for i := range amts {
|
||||
var found bool
|
||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||
require.True(t, found)
|
||||
}
|
||||
assert.Equal(t, sdk.Unbonded, validators[0].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[1].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[2].Status())
|
||||
assert.Equal(t, sdk.Bonded, validators[3].Status())
|
||||
assert.Equal(t, sdk.Unbonded, validators[4].Status())
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// test a swap in voting power
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 600)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
// clear the tracked changes to the gotValidator set
|
||||
func TestClearTendermintUpdates(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{100, 400, 200}
|
||||
validators := make([]types.Validator, len(amts))
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
keeper.UpdateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
assert.Equal(t, len(amts), len(updates))
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(updates))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesAllNone(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
// test from nothing to something
|
||||
// tendermintUpdate set: {} -> {c1, c3}
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
assert.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
|
||||
|
||||
// test from something to nothing
|
||||
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
keeper.RemoveValidator(ctx, validators[0].Owner)
|
||||
keeper.RemoveValidator(ctx, validators[1].Owner)
|
||||
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
|
||||
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
|
||||
assert.Equal(t, int64(0), updates[0].Power)
|
||||
assert.Equal(t, int64(0), updates[1].Power)
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesIdentical(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test identical,
|
||||
// tendermintUpdate set: {} -> {}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test single value change
|
||||
// tendermintUpdate set: {} -> {c1'}
|
||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(600))
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test multiple value change
|
||||
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 190)
|
||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 80)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates))
|
||||
require.Equal(t, validators[0].ABCIValidator(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[1].ABCIValidator(keeper.cdc), updates[1])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesInserted(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
|
||||
amts := []int64{10, 20, 5, 15, 25}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
validators[3] = keeper.UpdateValidator(ctx, validators[3])
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[3].ABCIValidator(keeper.cdc), updates[0])
|
||||
|
||||
// test validtor added at the end
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
validators[4] = keeper.UpdateValidator(ctx, validators[4])
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
require.Equal(t, validators[4].ABCIValidator(keeper.cdc), updates[0])
|
||||
}
|
||||
|
||||
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = 2
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
amts := []int64{10, 20, 5}
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||
keeper.SetPool(ctx, pool)
|
||||
}
|
||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
// test validator added at the end but not inserted in the valset
|
||||
// tendermintUpdate set: {} -> {}
|
||||
keeper.UpdateValidator(ctx, validators[2])
|
||||
updates := keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 0, len(updates))
|
||||
|
||||
// test validator change its power and become a gotValidator (pushing out an existing)
|
||||
// tendermintUpdate set: {} -> {c0, c4}
|
||||
keeper.ClearTendermintUpdates(ctx)
|
||||
assert.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 10)
|
||||
keeper.SetPool(ctx, pool)
|
||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||
|
||||
updates = keeper.GetTendermintUpdates(ctx)
|
||||
require.Equal(t, 2, len(updates), "%v", updates)
|
||||
require.Equal(t, validators[0].ABCIValidatorZero(keeper.cdc), updates[0])
|
||||
require.Equal(t, validators[2].ABCIValidator(keeper.cdc), updates[1])
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// TODO remove some of these prefixes once have working multistore
|
||||
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
ParamKey = []byte{0x00} // key for parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for the staking pools
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator by pubkey
|
||||
ValidatorsBondedKey = []byte{0x04} // prefix for each key to bonded/actively validating validators
|
||||
ValidatorsByPowerKey = []byte{0x05} // prefix for each key to a validator sorted by power
|
||||
ValidatorCliffKey = []byte{0x06} // key for block-local tx index
|
||||
ValidatorPowerCliffKey = []byte{0x07} // key for block-local tx index
|
||||
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
|
||||
DelegationKey = []byte{0x09} // prefix for each key to a delegator's bond
|
||||
IntraTxCounterKey = []byte{0x10} // key for block-local tx index
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
||||
// get the key for the validator with address
|
||||
func GetValidatorKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator with pubkey
|
||||
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
|
||||
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the current validator group, ordered like tendermint
|
||||
func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
|
||||
addr := pk.Address()
|
||||
return append(ValidatorsBondedKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator used in the power-store
|
||||
func GetValidatorsByPowerKey(validator Validator, pool Pool) []byte {
|
||||
|
||||
power := validator.EquivalentBondedShares(pool)
|
||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
revokedBytes := make([]byte, 1)
|
||||
if validator.Revoked {
|
||||
revokedBytes[0] = byte(0x01)
|
||||
} else {
|
||||
revokedBytes[0] = byte(0x00)
|
||||
}
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
return append(ValidatorsByPowerKey,
|
||||
append(revokedBytes,
|
||||
append(powerBytes,
|
||||
append(heightBytes,
|
||||
append(counterBytes, validator.Owner.Bytes()...)...)...)...)...) // TODO don't technically need to store owner
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
|
||||
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for delegator bond with validator
|
||||
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the prefix for a delegator for all validators
|
||||
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalBinary(&delegatorAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegationKey, res...)
|
||||
}
|
|
@ -1,792 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
addrDels = []sdk.Address{
|
||||
addrs[0],
|
||||
addrs[1],
|
||||
}
|
||||
addrVals = []sdk.Address{
|
||||
addrs[2],
|
||||
addrs[3],
|
||||
addrs[4],
|
||||
addrs[5],
|
||||
addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create a random pool
|
||||
pool.BondedTokens = sdk.NewInt(1234)
|
||||
pool.BondedShares = sdk.NewRat(124)
|
||||
pool.UnbondingTokens = sdk.NewInt(13934)
|
||||
pool.UnbondingShares = sdk.NewRat(145)
|
||||
pool.UnbondedTokens = sdk.NewInt(154)
|
||||
pool.UnbondedShares = sdk.NewRat(1333)
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// add a validator
|
||||
validator := NewValidator(addrVals[0], pks[0], Description{})
|
||||
validator, pool, delSharesCreated := validator.addTokensFromDel(pool, sdk.NewInt(100))
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.updateValidator(ctx, validator)
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// burn half the delegator shares
|
||||
validator, pool, burned := validator.removeDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||
assert.Equal(t, int64(50), burned.Int64())
|
||||
keeper.setPool(ctx, pool) // update the pool
|
||||
keeper.updateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
power = GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
}
|
||||
|
||||
func TestSetValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// test how the validator is set from a purely unbonbed pool
|
||||
validator := NewValidator(addrVals[0], pks[0], Description{})
|
||||
validator, pool, _ = validator.addTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.updateValidator(ctx, validator)
|
||||
|
||||
// after the save the validator should be bonded
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||
|
||||
// Check each store for being saved
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.True(ValEq(t, validator, resVal))
|
||||
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
resVals = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validator, resVals[0]))
|
||||
|
||||
updates := keeper.getTendermintUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
assert.Equal(t, validator.abciValidator(keeper.cdc), updates[0])
|
||||
|
||||
}
|
||||
|
||||
// This function tests updateValidator, GetValidator, GetValidatorsBonded, removeValidator
|
||||
func TestValidatorBasics(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//construct the validators
|
||||
var validators [3]Validator
|
||||
amts := []int64{9, 8, 7}
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrVals[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewUnbondedShares(sdk.ZeroRat())
|
||||
validators[i].addTokensFromDel(pool, sdk.NewInt(amt))
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
assert.Zero(t, len(resVals))
|
||||
|
||||
// set and retrieve a record
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
validators[0].PoolShares = NewBondedShares(sdk.NewRat(10))
|
||||
validators[0].DelegatorShares = sdk.NewRat(10)
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 1, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||
|
||||
// add other validators
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[1], resVal))
|
||||
resVal, found = keeper.GetValidator(ctx, addrVals[2])
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[2], resVal))
|
||||
|
||||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 3, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[2])) // order doesn't matter here
|
||||
assert.True(ValEq(t, validators[1], resVals[1]))
|
||||
assert.True(ValEq(t, validators[2], resVals[0]))
|
||||
|
||||
// remove a record
|
||||
keeper.removeValidator(ctx, validators[1].Owner)
|
||||
_, found = keeper.GetValidator(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetValidatorsByPower
|
||||
func GetValidatorSortingUnmixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].PoolShares = NewBondedShares(sdk.NewRat(amt))
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
|
||||
// test a basic increase in voting power
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(500))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(200))
|
||||
ctx = ctx.WithBlockHeight(10)
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators)
|
||||
assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators)
|
||||
|
||||
// no change in voting power - no change in sort
|
||||
ctx = ctx.WithBlockHeight(20)
|
||||
keeper.updateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
validators[4].PoolShares = NewBondedShares(sdk.NewRat(300))
|
||||
keeper.updateValidator(ctx, validators[3])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
ctx = ctx.WithBlockHeight(30)
|
||||
keeper.updateValidator(ctx, validators[4])
|
||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n, "%v", resValidators)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
}
|
||||
|
||||
func GetValidatorSortingMixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, sdk.NewInt(0))
|
||||
|
||||
// now 2 max resValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
|
||||
n := len(amts)
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].PoolShares = NewUnbondedShares(sdk.NewRat(amts[0]))
|
||||
validators[1].PoolShares = NewUnbondedShares(sdk.NewRat(amts[1]))
|
||||
validators[2].PoolShares = NewUnbondedShares(sdk.NewRat(amts[2]))
|
||||
validators[3].PoolShares = NewBondedShares(sdk.NewRat(amts[3]))
|
||||
validators[4].PoolShares = NewBondedShares(sdk.NewRat(amts[4]))
|
||||
for i := range amts {
|
||||
keeper.updateValidator(ctx, validators[i])
|
||||
}
|
||||
val0, found := keeper.GetValidator(ctx, addrs[0])
|
||||
require.True(t, found)
|
||||
val1, found := keeper.GetValidator(ctx, addrs[1])
|
||||
require.True(t, found)
|
||||
val2, found := keeper.GetValidator(ctx, addrs[2])
|
||||
require.True(t, found)
|
||||
val3, found := keeper.GetValidator(ctx, addrs[3])
|
||||
require.True(t, found)
|
||||
val4, found := keeper.GetValidator(ctx, addrs[4])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, sdk.Unbonded, val0.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val1.Status())
|
||||
assert.Equal(t, sdk.Unbonded, val2.Status())
|
||||
assert.Equal(t, sdk.Bonded, val3.Status())
|
||||
assert.Equal(t, sdk.Bonded, val4.Status())
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||
require.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].Owner, resValidators[3].Owner, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||
}
|
||||
|
||||
// TODO 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))
|
||||
}
|
243
x/stake/msg.go
243
x/stake/msg.go
|
@ -1,243 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// name to idetify transaction types
|
||||
const MsgType = "stake"
|
||||
|
||||
// XXX remove: think it makes more sense belonging with the Params so we can
|
||||
// initialize at genesis - to allow for the same tests we should should make
|
||||
// the ValidateBasic() function a return from an initializable function
|
||||
// ValidateBasic(bondDenom string) function
|
||||
const StakingToken = "steak"
|
||||
|
||||
//Verify interface at compile time
|
||||
var _, _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}, &MsgUnbond{}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgCreateValidator - struct for unbonding transactions
|
||||
type MsgCreateValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgCreateValidator(validatorAddr sdk.Address, pubkey crypto.PubKey,
|
||||
bond sdk.Coin, description Description) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
PubKey: pubkey,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgCreateValidator) Type() string { return MsgType }
|
||||
func (msg MsgCreateValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCreateValidator) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
PubKey: sdk.MustBech32ifyValPub(msg.PubKey),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Amount.Sign() != 1 {
|
||||
return ErrBadBondingAmount(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(DefaultCodespace, CodeInvalidInput, "description must be included")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgEditValidator - struct for editing a validator
|
||||
type MsgEditValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
}
|
||||
|
||||
func NewMsgEditValidator(validatorAddr sdk.Address, description Description) MsgEditValidator {
|
||||
return MsgEditValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: validatorAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgEditValidator) Type() string { return MsgType }
|
||||
func (msg MsgEditValidator) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.ValidatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgEditValidator) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgEditValidator) ValidateBasic() sdk.Error {
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
return newError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgDelegate) Type() string { return MsgType }
|
||||
func (msg MsgDelegate) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.DelegatorAddr}
|
||||
}
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgDelegate) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Bond: msg.Bond,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgDelegate) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Amount.Sign() != 1 {
|
||||
return ErrBadBondingAmount(DefaultCodespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgUnbond - struct for unbonding transactions
|
||||
type MsgUnbond struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
func NewMsgUnbond(delegatorAddr, validatorAddr sdk.Address, shares string) MsgUnbond {
|
||||
return MsgUnbond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: shares,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgUnbond) Type() string { return MsgType }
|
||||
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgUnbond) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Shares string `json:"shares"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Shares: msg.Shares,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
func (msg MsgUnbond) ValidateBasic() sdk.Error {
|
||||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Shares != "MAX" {
|
||||
rat, err := sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return ErrBadShares(DefaultCodespace)
|
||||
}
|
||||
if rat.IsZero() || rat.LT(sdk.ZeroRat()) {
|
||||
return ErrBadShares(DefaultCodespace)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
coinPos = sdk.NewCoin("steak", 1000)
|
||||
coinZero = sdk.NewCoin("steak", 0)
|
||||
coinNeg = sdk.NewCoin("steak", -10000)
|
||||
coinPosNotAtoms = sdk.NewCoin("foo", 10000)
|
||||
coinZeroNotAtoms = sdk.NewCoin("foo", 0)
|
||||
coinNegNotAtoms = sdk.NewCoin("foo", -10000)
|
||||
)
|
||||
|
||||
// test ValidateBasic for MsgCreateValidator
|
||||
func TestMsgCreateValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
pubkey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addrs[0], pks[0], coinPos, true},
|
||||
{"partial description", "", "", "c", "", addrs[0], pks[0], coinPos, true},
|
||||
{"empty description", "", "", "", "", addrs[0], pks[0], coinPos, false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, pks[0], coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", addrs[0], emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", addrs[0], pks[0], coinZero, false},
|
||||
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
|
||||
{"negative bond", "a", "b", "c", "d", addrs[0], pks[0], coinNeg, false},
|
||||
{"wrong staking token", "a", "b", "c", "d", addrs[0], pks[0], coinPosNotAtoms, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgEditValidator
|
||||
func TestMsgEditValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
validatorAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addrs[0], true},
|
||||
{"partial description", "", "", "c", "", addrs[0], true},
|
||||
{"empty description", "", "", "", "", addrs[0], false},
|
||||
{"empty address", "a", "b", "c", "d", emptyAddr, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgEditValidator(tc.validatorAddr, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgDelegate
|
||||
func TestMsgDelegate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", addrs[0], addrs[1], coinPos, true},
|
||||
{"self bond", addrs[0], addrs[0], coinPos, true},
|
||||
{"empty delegator", emptyAddr, addrs[0], coinPos, false},
|
||||
{"empty validator", addrs[0], emptyAddr, coinPos, false},
|
||||
{"empty bond", addrs[0], addrs[1], coinZero, false},
|
||||
{"negative bond", addrs[0], addrs[1], coinNeg, false},
|
||||
{"wrong staking token", addrs[0], addrs[1], coinPosNotAtoms, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test ValidateBasic for MsgUnbond
|
||||
func TestMsgUnbond(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
shares string
|
||||
expectPass bool
|
||||
}{
|
||||
{"max unbond", addrs[0], addrs[1], "MAX", true},
|
||||
{"decimal unbond", addrs[0], addrs[1], "0.1", true},
|
||||
{"negative decimal unbond", addrs[0], addrs[1], "-0.1", false},
|
||||
{"zero unbond", addrs[0], addrs[1], "0.0", false},
|
||||
{"invalid decimal", addrs[0], addrs[0], "sunny", false},
|
||||
{"empty delegator", emptyAddr, addrs[0], "0.1", false},
|
||||
{"empty validator", addrs[0], emptyAddr, "0.1", false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgUnbond(tc.delegatorAddr, tc.validatorAddr, tc.shares)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
assert.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO introduce with go-amino
|
||||
//func TestSerializeMsg(t *testing.T) {
|
||||
|
||||
//// make sure all types construct properly
|
||||
//bondAmt := 1234321
|
||||
//bond := sdk.Coin{Denom: "atom", Amount: int64(bondAmt)}
|
||||
|
||||
//tests := []struct {
|
||||
//tx sdk.Msg
|
||||
//}{
|
||||
//{NewMsgCreateValidator(addrs[0], pks[0], bond, Description{})},
|
||||
//{NewMsgEditValidator(addrs[0], Description{})},
|
||||
//{NewMsgDelegate(addrs[0], addrs[1], bond)},
|
||||
//{NewMsgUnbond(addrs[0], addrs[1], strconv.Itoa(bondAmt))},
|
||||
//}
|
||||
|
||||
//for i, tc := range tests {
|
||||
//var tx sdk.Tx
|
||||
//bs := wire.BinaryBytes(tc.tx)
|
||||
//err := wire.ReadBinaryBytes(bs, &tx)
|
||||
//if assert.NoError(t, err, "%d", i) {
|
||||
//assert.Equal(t, tc.tx, tx, "%d", i)
|
||||
//}
|
||||
//}
|
||||
//}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue