Merge with develop

This commit is contained in:
HaoyangLiu 2018-10-04 08:10:29 +08:00
commit 1d4cfe89d2
103 changed files with 2000 additions and 2151 deletions

78
Gopkg.lock generated
View File

@ -34,11 +34,11 @@
[[projects]]
branch = "master"
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "d81d8877b8f327112e94e814937143a71d1692a7"
revision = "2a560b2036bee5e3679ec2133eb6520b2f195213"
[[projects]]
digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2"
@ -95,12 +95,12 @@
version = "v0.3.0"
[[projects]]
digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406"
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
name = "github.com/go-stack/stack"
packages = ["."]
pruneopts = "UT"
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
version = "v1.8.0"
[[projects]]
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
@ -164,13 +164,13 @@
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
@ -179,7 +179,8 @@
"json/token",
]
pruneopts = "UT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
version = "v1.0.0"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
@ -214,12 +215,12 @@
version = "v1.8.0"
[[projects]]
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
version = "v0.0.4"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
@ -238,12 +239,12 @@
version = "v1.0.0"
[[projects]]
digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4"
digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
version = "v1.0.0"
revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad"
version = "v1.1.1"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
@ -301,7 +302,7 @@
[[projects]]
branch = "master"
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef"
name = "github.com/prometheus/procfs"
packages = [
".",
@ -310,7 +311,7 @@
"xfs",
]
pruneopts = "UT"
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2"
[[projects]]
digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64"
@ -328,15 +329,15 @@
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
version = "v1.1.2"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
@ -355,12 +356,12 @@
version = "v0.0.1"
[[projects]]
branch = "master"
digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a"
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2"
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
version = "v1.0.0"
[[projects]]
digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9"
@ -410,6 +411,13 @@
pruneopts = "UT"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
[[projects]]
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
name = "github.com/tendermint/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
[[projects]]
branch = "master"
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
@ -423,12 +431,12 @@
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
digest = "1:2c971a45c89ca2ccc735af50919cdee05fbdc54d4bf50625073693300e31ead8"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee"
version = "v0.12.0"
[[projects]]
digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430"
@ -439,7 +447,7 @@
version = "v0.11.0"
[[projects]]
digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6"
digest = "1:f4fcc1a4dbe079b200556ca26c1ff1dacf23712125b9c265d8f02c0dbc318f39"
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
@ -457,6 +465,8 @@
"crypto/ed25519",
"crypto/encoding/amino",
"crypto/merkle",
"crypto/multisig",
"crypto/multisig/bitarray",
"crypto/secp256k1",
"crypto/tmhash",
"crypto/xsalsa20symmetric",
@ -476,7 +486,6 @@
"lite",
"lite/client",
"lite/errors",
"lite/files",
"lite/proxy",
"mempool",
"node",
@ -499,11 +508,12 @@
"state/txindex/kv",
"state/txindex/null",
"types",
"types/time",
"version",
]
pruneopts = "UT"
revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
version = "v0.23.1-rc0"
revision = "d419fffe18531317c28c29a292ad7d253f6cafdf"
version = "v0.24.0"
[[projects]]
digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666"
@ -534,7 +544,7 @@
"salsa20/salsa",
]
pruneopts = "UT"
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
@ -554,14 +564,14 @@
[[projects]]
branch = "master"
digest = "1:ead82e3e398388679f3ad77633a087ac31a47a6be59ae20841e1d1b3a3fbbd22"
digest = "1:8bc8ecef1d63576cfab4d08b44a1f255dd67e5b019b7a44837d62380f266a91c"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "4ea2f632f6e912459fe60b26b1749377f0d889d5"
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -588,11 +598,11 @@
[[projects]]
branch = "master"
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
digest = "1:1e6b0176e8c5dd8ff551af65c76f8b73a99bcf4d812cedff1b91711b7df4804c"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
revision = "c7e5094acea1ca1b899e2259d80a6b0f882f81f8"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"

View File

@ -49,7 +49,7 @@
[[override]]
name = "github.com/tendermint/go-amino"
version = "=v0.12.0-rc0"
version = "=v0.12.0"
[[override]]
name = "github.com/tendermint/iavl"
@ -57,7 +57,7 @@
[[override]]
name = "github.com/tendermint/tendermint"
version = "=v0.23.1-rc0"
version = "=0.24.0"
[[constraint]]
name = "github.com/bartekn/go-bip39"

View File

@ -182,7 +182,7 @@ test_cover:
test_lint:
gometalinter.v2 --config=tools/gometalinter.json ./...
!(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
!(gometalinter.v2 --exclude /usr/lib/go/src/ --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
dep status >> /dev/null
!(grep -n branch Gopkg.toml)

View File

@ -42,6 +42,10 @@ BREAKING CHANGES
* [simulation] \#2162 Added back correct supply invariants
* SDK
* [core] \#2219 Update to Tendermint 0.24.0
* Validator set updates delayed by one block
* BFT timestamp that can safely be used by applications
* Fixed maximum block size enforcement
* [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal
* [types] [\#1901](https://github.com/cosmos/cosmos-sdk/issues/1901) Validator interface's GetOwner() renamed to GetOperator()
* [x/slashing] [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122) - Implement slashing period
@ -60,6 +64,7 @@ BREAKING CHANGES
* [x/auth] \#2377 auth.StdSignMsg -> txbuilder.StdSignMsg
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
* [x/staking] \#2236 more distribution hooks for distribution
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
* Tendermint
@ -139,6 +144,7 @@ IMPROVEMENTS
* [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282
* [simulation] Add a concept of weighting the operations \#2303
* [simulation] Logs get written to file if large, and also get printed on panics \#2285
* [simulation] Bank simulations now makes testing auth configurable \#2425
* [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable
* [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149)
* [store] [\#2017](https://github.com/cosmos/cosmos-sdk/issues/2017) Refactor
@ -169,5 +175,7 @@ BUG FIXES
* [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker`
* [simulation] \#1924 Make simulation stop on SIGTERM
* [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository.
* [\#2416](https://github.com/cosmos/cosmos-sdk/issues/2416) Refactored
`InitializeTestLCD` to properly include proposing validator in genesis state.
* Tendermint

View File

@ -22,7 +22,7 @@ breaking changes.
## Gaia Testnet
To join the latest testnet, follow
[the guide](https://cosmos.network/docs/getting-started/full-node.html#setting-up-a-new-node).
[the guide](./docs/getting-started/join-testnet.md).
For status updates and genesis files, see the
[testnets repo](https://github.com/cosmos/testnets).
@ -30,15 +30,14 @@ For status updates and genesis files, see the
## Install
See the
[install instructions](https://cosmos.network/docs/getting-started/installation.html).
[install instructions](./docs/getting-started/installation.md).
## Quick Start
See the [Cosmos Docs](https://cosmos.network/docs/)
- [Getting started with the SDK](https://cosmos.network/docs/sdk/core/intro.html)
- [Getting started with the SDK](./docs/sdk/core/intro.md)
- [SDK Examples](/examples)
- [Join the testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node)
## Disambiguation

View File

@ -64,9 +64,9 @@ type BaseApp struct {
// checkState is set on initialization and reset on Commit.
// deliverState is set in InitChain and BeginBlock and cleared on Commit.
// See methods setCheckState and setDeliverState.
checkState *state // for CheckTx
deliverState *state // for DeliverTx
signedValidators []abci.SigningValidator // absent validators from begin block
checkState *state // for CheckTx
deliverState *state // for DeliverTx
voteInfos []abci.VoteInfo // absent validators from begin block
// minimum fees for spam prevention
minimumFees sdk.Coins
@ -435,7 +435,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
// set the signed validators for addition to context in deliverTx
// TODO: communicate this result to the address to pubkey map in slashing
app.signedValidators = req.LastCommitInfo.GetValidators()
app.voteInfos = req.LastCommitInfo.GetVotes()
return
}
@ -509,12 +509,12 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error {
}
// retrieve the context for the ante handler and store the tx bytes; store
// the signing validators if the tx runs within the deliverTx() state.
// the vote infos if the tx runs within the deliverTx() state.
func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) {
// Get the context
ctx = getState(app, mode).ctx.WithTxBytes(txBytes)
if mode == runTxModeDeliver {
ctx = ctx.WithSigningValidators(app.signedValidators)
ctx = ctx.WithVoteInfos(app.voteInfos)
}
return
}

View File

@ -1,15 +1,15 @@
package client
import (
"github.com/spf13/cobra"
"github.com/mitchellh/go-homedir"
"bufio"
"path"
"os"
"io/ioutil"
"github.com/pelletier/go-toml"
"fmt"
"github.com/cosmos/cosmos-sdk/types"
"github.com/mitchellh/go-homedir"
"github.com/pelletier/go-toml"
"github.com/spf13/cobra"
"io/ioutil"
"os"
"path"
)
type cliConfig struct {
@ -34,7 +34,7 @@ func ConfigCmd() *cobra.Command {
return cfg
}
func runConfigCmd(cmd *cobra.Command, args [] string) error {
func runConfigCmd(cmd *cobra.Command, args []string) error {
home, err := homedir.Dir()
if err != nil {
return err

View File

@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
@ -15,14 +17,18 @@ import (
cskeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/log"
tmlite "github.com/tendermint/tendermint/lite"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"os"
)
const ctxAccStoreName = "acc"
var (
verifier tmlite.Verifier
)
// CLIContext implements a typical CLI context created in SDK modules for
// transaction handling and queries.
type CLIContext struct {
@ -39,7 +45,7 @@ type CLIContext struct {
Async bool
JSON bool
PrintResponse bool
Certifier tmlite.Certifier
Verifier tmlite.Verifier
DryRun bool
GenerateOnly bool
fromAddress types.AccAddress
@ -60,6 +66,11 @@ func NewCLIContext() CLIContext {
from := viper.GetString(client.FlagFrom)
fromAddress, fromName := fromFields(from)
// We need to use a single verifier for all contexts
if verifier == nil {
verifier = createVerifier()
}
return CLIContext{
Client: rpc,
Output: os.Stdout,
@ -72,7 +83,7 @@ func NewCLIContext() CLIContext {
Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
Certifier: createCertifier(),
Verifier: verifier,
DryRun: viper.GetBool(client.FlagDryRun),
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
fromAddress: fromAddress,
@ -81,7 +92,12 @@ func NewCLIContext() CLIContext {
}
}
func createCertifier() tmlite.Certifier {
func createVerifier() tmlite.Verifier {
trustNodeDefined := viper.IsSet(client.FlagTrustNode)
if !trustNodeDefined {
return nil
}
trustNode := viper.GetBool(client.FlagTrustNode)
if trustNode {
return nil
@ -105,15 +121,16 @@ func createCertifier() tmlite.Certifier {
fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String())
os.Exit(1)
}
node := rpcclient.NewHTTP(nodeURI, "/websocket")
verifier, err := tmliteProxy.NewVerifier(chainID, filepath.Join(home, ".gaialite"), node, log.NewNopLogger())
certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI)
if err != nil {
fmt.Printf("Create certifier failed: %s\n", err.Error())
fmt.Printf("Create verifier failed: %s\n", err.Error())
fmt.Printf("Please check network connection and verify the address of the node to connect to\n")
os.Exit(1)
}
return certifier
return verifier
}
func fromFields(from string) (fromAddr types.AccAddress, fromName string) {
@ -204,8 +221,8 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
return ctx
}
// WithCertifier - return a copy of the context with an updated Certifier
func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext {
ctx.Certifier = certifier
// WithVerifier - return a copy of the context with an updated Verifier
func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext {
ctx.Verifier = verifier
return ctx
}

View File

@ -14,10 +14,10 @@ import (
"github.com/cosmos/cosmos-sdk/store"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/lite"
tmliteErr "github.com/tendermint/tendermint/lite/errors"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
tmtypes "github.com/tendermint/tendermint/types"
)
// GetNode returns an RPC client. If the context's client is not defined, an
@ -184,14 +184,14 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
return resp.Value, nil
}
// Certify verifies the consensus proof at given height.
func (ctx CLIContext) Certify(height int64) (lite.Commit, error) {
check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Certifier)
// Verify verifies the consensus proof at given height.
func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) {
check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier)
switch {
case tmliteErr.IsCommitNotFoundErr(err):
return lite.Commit{}, ErrVerifyCommit(height)
case tmliteErr.IsErrCommitNotFound(err):
return tmtypes.SignedHeader{}, ErrVerifyCommit(height)
case err != nil:
return lite.Commit{}, err
return tmtypes.SignedHeader{}, err
}
return check, nil
@ -199,12 +199,12 @@ func (ctx CLIContext) Certify(height int64) (lite.Commit, error) {
// verifyProof perform response proof verification.
func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error {
if ctx.Certifier == nil {
if ctx.Verifier == nil {
return fmt.Errorf("missing valid certifier to verify data from distrusted node")
}
// the AppHash for height H is in header H+1
commit, err := ctx.Certify(resp.Height + 1)
commit, err := ctx.Verify(resp.Height + 1)
if err != nil {
return err
}

View File

@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
IsCA: true,
}
hosts := strings.Split(host, ",")
for _, h := range hosts {

View File

@ -42,7 +42,7 @@ func init() {
func TestKeys(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// get seed
@ -125,7 +125,7 @@ func TestVersion(t *testing.T) {
t.SkipNow()
}
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
// node info
@ -148,7 +148,7 @@ func TestVersion(t *testing.T) {
}
func TestNodeStatus(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
// node info
@ -170,7 +170,7 @@ func TestNodeStatus(t *testing.T) {
}
func TestBlock(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
var resultBlock ctypes.ResultBlock
@ -200,7 +200,7 @@ func TestBlock(t *testing.T) {
}
func TestValidators(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
var resultVals rpc.ResultValidatorsOutput
@ -235,7 +235,7 @@ func TestValidators(t *testing.T) {
func TestCoinSend(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
@ -303,7 +303,7 @@ func TestCoinSend(t *testing.T) {
func DisabledTestIBCTransfer(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
acc := getAccount(t, port, addr)
@ -332,7 +332,7 @@ func DisabledTestIBCTransfer(t *testing.T) {
func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
acc := getAccount(t, port, addr)
@ -392,7 +392,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
func TestTxs(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// query wrong
@ -451,7 +451,7 @@ func TestTxs(t *testing.T) {
func TestPoolParamsQuery(t *testing.T) {
_, password := "test", "1234567890"
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
defaultParams := stake.DefaultParams()
@ -485,9 +485,11 @@ func TestPoolParamsQuery(t *testing.T) {
}
func TestValidatorsQuery(t *testing.T) {
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
require.Equal(t, 1, len(pks))
require.Equal(t, 1, len(valPubKeys))
require.Equal(t, 1, len(operAddrs))
validators := getValidators(t, port)
require.Equal(t, len(validators), 1)
@ -495,35 +497,35 @@ func TestValidatorsQuery(t *testing.T) {
// make sure all the validators were found (order unknown because sorted by operator addr)
foundVal := false
if validators[0].ConsPubKey == pks[0] {
if validators[0].ConsPubKey == valPubKeys[0] {
foundVal = true
}
require.True(t, foundVal, "pk %v, operator %v", pks[0], validators[0].OperatorAddr)
require.True(t, foundVal, "pk %v, operator %v", operAddrs[0], validators[0].OperatorAddr)
}
func TestValidatorQuery(t *testing.T) {
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
require.Equal(t, 1, len(pks))
require.Equal(t, 1, len(valPubKeys))
require.Equal(t, 1, len(operAddrs))
validator1Operator := sdk.ValAddress(pks[0].Address())
validator := getValidator(t, port, validator1Operator)
assert.Equal(t, validator.OperatorAddr, validator1Operator, "The returned validator does not hold the correct data")
validator := getValidator(t, port, operAddrs[0])
assert.Equal(t, validator.OperatorAddr, operAddrs[0], "The returned validator does not hold the correct data")
}
func TestBonding(t *testing.T) {
name, password, denom := "test", "1234567890", "steak"
addr, seed := CreateAddr(t, name, password, GetKeyBase(t))
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
amt := sdk.NewDec(60)
validator1Operator := sdk.ValAddress(pks[0].Address())
validator := getValidator(t, port, validator1Operator)
validator := getValidator(t, port, operAddrs[0])
// create bond TX
resultTx := doDelegate(t, port, seed, name, password, addr, validator1Operator, 60)
resultTx := doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60)
tests.WaitForHeight(resultTx.Height+1, port)
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -535,7 +537,7 @@ func TestBonding(t *testing.T) {
require.Equal(t, int64(40), coins.AmountOf(denom).Int64())
// query validator
bond := getDelegation(t, port, addr, validator1Operator)
bond := getDelegation(t, port, addr, operAddrs[0])
require.Equal(t, amt, bond.Shares)
summary := getDelegationSummary(t, port, addr)
@ -546,17 +548,17 @@ func TestBonding(t *testing.T) {
bondedValidators := getDelegatorValidators(t, port, addr)
require.Len(t, bondedValidators, 1)
require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr)
require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr)
require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String())
bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator)
require.Equal(t, validator1Operator, bondedValidator.OperatorAddr)
bondedValidator := getDelegatorValidator(t, port, addr, operAddrs[0])
require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr)
//////////////////////
// testing unbonding
// create unbond TX
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Operator, 60)
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, operAddrs[0], 60)
tests.WaitForHeight(resultTx.Height+1, port)
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -567,7 +569,7 @@ func TestBonding(t *testing.T) {
coins = acc.GetCoins()
require.Equal(t, int64(40), coins.AmountOf("steak").Int64())
unbonding := getUndelegation(t, port, addr, validator1Operator)
unbonding := getUndelegation(t, port, addr, operAddrs[0])
require.Equal(t, "60", unbonding.Balance.Amount.String())
summary = getDelegationSummary(t, port, addr)
@ -600,7 +602,7 @@ func TestBonding(t *testing.T) {
func TestSubmitProposal(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// create SubmitProposal TX
@ -622,7 +624,7 @@ func TestSubmitProposal(t *testing.T) {
func TestDeposit(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// create SubmitProposal TX
@ -656,7 +658,7 @@ func TestDeposit(t *testing.T) {
func TestVote(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// create SubmitProposal TX
@ -694,12 +696,12 @@ func TestVote(t *testing.T) {
func TestUnjail(t *testing.T) {
_, password := "test", "1234567890"
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// XXX: any less than this and it fails
tests.WaitForHeight(3, port)
pkString, _ := sdk.Bech32ifyConsPub(pks[0])
pkString, _ := sdk.Bech32ifyConsPub(valPubKeys[0])
signingInfo := getSigningInfo(t, port, pkString)
tests.WaitForHeight(4, port)
require.Equal(t, true, signingInfo.IndexOffset > 0)
@ -712,7 +714,7 @@ func TestProposalsQuery(t *testing.T) {
name2, password2 := "test2", "1234567890"
addr, seed := CreateAddr(t, "test", password1, GetKeyBase(t))
addr2, seed2 := CreateAddr(t, "test2", password2, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2})
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2})
defer cleanup()
// Addr1 proposes (and deposits) proposals #1 and #2

View File

@ -33,6 +33,7 @@ import (
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
nm "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/p2p"
pvm "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
@ -114,7 +115,14 @@ func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.Acc
// their respective sockets where nValidators is the total number of validators
// and initAddrs are the accounts to initialize with some steak tokens. It
// returns a cleanup function, a set of validator public keys, and a port.
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) {
func InitializeTestLCD(
t *testing.T, nValidators int, initAddrs []sdk.AccAddress,
) (cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) {
if nValidators < 1 {
panic("InitializeTestLCD must use at least one validator")
}
config := GetConfig()
config.Consensus.TimeoutCommit = 100
config.Consensus.SkipTimeoutCommit = false
@ -135,30 +143,34 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
require.NoError(t, err)
if nValidators < 1 {
panic("InitializeTestLCD must use at least one validator")
// append initial (proposing) validator
genDoc.Validators[0] = tmtypes.GenesisValidator{
PubKey: privVal.GetPubKey(),
Power: 999999, // create enough power to enable 2/3 voting power
Name: "validator-1",
}
// append any additional (non-proposing) validators
for i := 1; i < nValidators; i++ {
genDoc.Validators = append(genDoc.Validators,
tmtypes.GenesisValidator{
PubKey: ed25519.GenPrivKey().PubKey(),
Power: 1,
Name: "val",
Name: fmt.Sprintf("validator-%d", i+1),
},
)
}
var validatorsPKs []crypto.PubKey
// NOTE: It's bad practice to reuse public key address for the operator
// address but doing in the test for simplicity.
var appGenTxs []json.RawMessage
for _, gdValidator := range genDoc.Validators {
pk := gdValidator.PubKey
validatorsPKs = append(validatorsPKs, pk)
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1")
for _, gdValidator := range genDoc.Validators {
operAddr := ed25519.GenPrivKey().PubKey().Address()
pk := gdValidator.PubKey
valConsPubKeys = append(valConsPubKeys, pk)
valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr))
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(operAddr), gdValidator.Name)
require.NoError(t, err)
appGenTxs = append(appGenTxs, appGenTx)
@ -186,7 +198,8 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
// XXX: Need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress)
viper.Set(client.FlagChainID, genDoc.ChainID)
viper.Set(client.FlagTrustNode, false)
// TODO Set to false once the upstream Tendermint proof verification issue is fixed.
viper.Set(client.FlagTrustNode, true)
dir, err := ioutil.TempDir("", "lcd_test")
require.NoError(t, err)
viper.Set(cli.HomeFlag, dir)
@ -201,14 +214,14 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
tests.WaitForLCDStart(port)
tests.WaitForHeight(1, port)
cleanup := func() {
cleanup = func() {
logger.Debug("cleaning up LCD initialization")
node.Stop()
node.Wait()
lcd.Close()
}
return cleanup, validatorsPKs, port
return cleanup, valConsPubKeys, valOperAddrs, port
}
// startTM creates and starts an in-process Tendermint node with memDB and
@ -222,9 +235,14 @@ func startTM(
) (*nm.Node, error) {
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil }
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil }
nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile())
if err != nil {
return nil, err
}
node, err := nm.NewNode(
tmcfg,
privVal,
nodeKey,
proxy.NewLocalClientCreator(app),
genDocProvider,
dbProvider,

View File

@ -47,7 +47,7 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
}
if !cliCtx.TrustNode {
check, err := cliCtx.Certify(res.Block.Height)
check, err := cliCtx.Verify(res.Block.Height)
if err != nil {
return nil, err
}

View File

@ -9,9 +9,9 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/spf13/viper"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
func statusCommand() *cobra.Command {

View File

@ -12,8 +12,8 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/spf13/viper"
tmtypes "github.com/tendermint/tendermint/types"
)
// TODO these next two functions feel kinda hacky based on their placement
@ -76,12 +76,12 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) {
}
if !cliCtx.TrustNode {
check, err := cliCtx.Certify(validatorsRes.BlockHeight)
check, err := cliCtx.Verify(validatorsRes.BlockHeight)
if err != nil {
return nil, err
}
if !bytes.Equal(check.ValidatorsHash(), tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) {
if !bytes.Equal(check.ValidatorsHash, tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) {
return nil, fmt.Errorf("got invalid validatorset")
}
}

View File

@ -3,8 +3,8 @@ package tx
import (
"encoding/hex"
"fmt"
"net/http"
"github.com/tendermint/tendermint/libs/common"
"net/http"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
@ -87,7 +87,7 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([]
// ValidateTxResult performs transaction verification
func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error {
check, err := cliCtx.Certify(res.Height)
check, err := cliCtx.Verify(res.Height)
if err != nil {
return err
}

View File

@ -31,7 +31,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
}
// Initialize the chain
vals := []abci.Validator{}
vals := []abci.ValidatorUpdate{}
gapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
gapp.Commit()

View File

@ -31,5 +31,5 @@ func ExampleTxSendSize() {
fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1})))
fmt.Println(len(cdc.MustMarshalBinaryBare(tx)))
// output: 80
// 173
// 167
}

View File

@ -90,7 +90,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
return []simulation.WeightedOperation{
{100, banksim.SimulateSingleInputMsgSend(app.accountMapper)},
{100, banksim.SingleInputSendMsg(app.accountMapper, app.bankKeeper)},
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)},
{100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)},
{100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)},

View File

@ -7,8 +7,8 @@ import (
"fmt"
"io/ioutil"
"os"
"testing"
"path"
"testing"
"github.com/stretchr/testify/require"

View File

@ -19,9 +19,9 @@ import (
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"path"
"os"
"github.com/spf13/viper"
"os"
"path"
)
const (
@ -153,7 +153,7 @@ func initConfig(cmd *cobra.Command) error {
}
if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil {
return err
return err
}
return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag))
}

View File

@ -55,6 +55,7 @@ func ExamplePrintRegisteredTypes() {
//| PrivKeyLedgerSecp256k1 | tendermint/PrivKeyLedgerSecp256k1 | 0x10CAB393 | variable | |
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | |
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | |
//| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | |
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | |
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | |
}

View File

@ -9,12 +9,12 @@ import (
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/bip39"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/crypto/secp256k1"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/cosmos/cosmos-sdk/types"
)
var _ Keybase = dbKeybase{}

View File

@ -10,8 +10,8 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/cosmos/cosmos-sdk/types"
dbm "github.com/tendermint/tendermint/libs/db"
)
func init() {
@ -403,4 +403,4 @@ func ExampleNew() {
func accAddr(info Info) types.AccAddress {
return (types.AccAddress)(info.GetPubKey().Address())
}
}

View File

@ -15,7 +15,8 @@ module.exports = {
collapsable: false,
children: [
"/introduction/cosmos-hub",
"/introduction/tendermint",
"/introduction/tendermint-cosmos",
"/introduction/tendermint"
]
},
{
@ -54,6 +55,18 @@ module.exports = {
// "/specs/icts",
// ]
// },
{
title: "SDK by Examples - Simple Governance",
collapsable: false,
children: [
["/sdk/sdk-by-examples/simple-governance/intro", "Intro"],
"/sdk/sdk-by-examples/simple-governance/setup-and-design",
"/sdk/sdk-by-examples/simple-governance/app-init",
"/sdk/sdk-by-examples/simple-governance/simple-gov-module",
"/sdk/sdk-by-examples/simple-governance/bridging-it-all",
"/sdk/sdk-by-examples/simple-governance/running-the-application"
]
},
{
title: "Lotion JS",
collapsable: false,
@ -71,6 +84,13 @@ module.exports = {
"/validators/validator-faq"
]
},
{
title: "Clients",
collapsable: false,
children: [
["/clients/service-providers", "Service Providers"]
]
},
{
title: "Resources",
collapsable: false,

View File

@ -1,75 +1,44 @@
# High priority
## Fees
- Collection
- Simple flat fee set in-config by validators & full nodes - ref [#1921](https://github.com/cosmos/cosmos-sdk/issues/1921)
- @sunnya97 working on implementation
- _*BLOCKER:*_ Blocked on [tendermint/tendermint#2275](https://github.com/tendermint/tendermint/issues/2275) @ValarDragon
- Distribution
- "Piggy bank" fee distribution - ref [#1944](https://github.com/cosmos/cosmos-sdk/pull/1944) (spec)
- @rigelrozanski working on implementation
- EST TIMELINE:
- Work on fees should be completed in the `v0.25.0` release
# Post-0.25/GoS Pre-Release
## Staking/Slashing/Stability
- Unbonding state for validators (WIP) [#2163](https://github.com/cosmos/cosmos-sdk/pull/2163) @rigelrozanski
- Needs :eyes: from @chris
- Should be in `v0.25.0` release
- Slashing period PR - ref [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122)
- Needs :eyes: from @cwgoes and @jaekwon
- Should be in `v0.25.0` release
- Other slashing issues blocking for launch - [#1256](https://github.com/cosmos/cosmos-sdk/issues/1256)
- Update staking/slashing for NextValSet change
- @cwgoes to start next
- Miscellaneous minor staking issues
- [List here](https://github.com/cosmos/cosmos-sdk/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Astaking+label%3Aprelaunch)
- Need to figure out scope of work here to estimate time
- @rigelrozanski to start next
## Vesting
- Single `VestingAccount` allowing delegation/voting but no withdrawals
- Ref [#1875](https://github.com/cosmos/cosmos-sdk/pull/1875) (spec)
- @AdityaSripal working on this.
- Should be in `v0.25.0` release
- Consider "tombstone" / "prison" - double-sign and you can never validate again - https://github.com/cosmos/cosmos-sdk/issues/2363
## Multisig
- Already implemented on TM side, need simple CLI interface
- @alessio working on the SDK side of things here
- Need to schedule some time with @alessio, @ebuchman and @ValarDragon this week to finalize feature set/implementation plan
- Need to test changes in https://github.com/cosmos/cosmos-sdk/pull/2165
- Spam prevention - https://github.com/cosmos/cosmos-sdk/issues/2019
## ABCI Changes
- Need to update for new ABCI changes - error string, tags are list of lists, proposer in header (Tendermint 0.24?)
- @cwgoes has done some work here. Should be on `develop` in tendermint w/in next week.
- Include in tendermint `v0.24.0` release?
- Need to update for new ABCI changes when/if they land - error string, tags are list of lists
- Need to verify correct proposer reward semantics
- CheckEvidence/DeliverEvidence, CheckTx/DeliverTx ordering semantics
## Gas
- Simple transaction benchmarking work by @jlandrews to inform additional work here
- Integrate @alessio's simulation work into CLI and LCD
- Sanity Checks
- Charge for transaction size
- Decide what "one gas" corresponds to (standard hardware benchmarks?)
- More benchmarking
- Consider charging based on maximum depth of IAVL tree iteration
- Test out gas estimation in CLI and LCD and ensure the UX works
## LCD
- Bianje working on implementation ([#2147](https://github.com/cosmos/cosmos-sdk/pull/2147))
- ICS 0,ICS 1, ICS 20 and ICS 21 implemented in this PR :point_up:
- @fedekunze, @jackzampolin and @alexanderbez to review
- Bianje working on implementation of ICS standards
- Additional PR incoming for ICS 22 and ICS 23
- Include [#382](https://github.com/cosmos/cosmos-sdk/issues/382)
- Decide what ought to be ICS-standardized and what ought not to
# Lower priority
## Governance v2
- Simple software upgrade proposals
- Implementation described in [#1079](https://github.com/cosmos/cosmos-sdk/issues/1079)
- Agree upon a block height to switch to new version
- Another Governance proposal from @jaekwon [#2116](https://github.com/cosmos/cosmos-sdk/pull/2116)
- Circuit breaker
- Circuit breaker - https://github.com/cosmos/cosmos-sdk/issues/926
- Parameter change proposals (roughly the same implementation as circuit breaker)
## Documentation

View File

@ -8,6 +8,12 @@ The first blockchain in the Cosmos Network is the Cosmos Hub, whose native token
Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.
## Quick Start
- [Getting started with the SDK](./sdk/core/intro.md)
- [SDK Examples](../examples)
- [Join the testnet](./getting-started/join-testnet.md#run-a-full-node)
## Edit the Documentation
See [this file](./DOCS_README.md) for details of the build process and

View File

@ -1,6 +1,6 @@
# Install the SDK
This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](full-node.md) or a [Validator](/validators/validator-setup.md).
This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](./join-testnet.md#run-a-full-node) or a [Validator](/validators/validator-setup.md).
## Install Go

View File

@ -9,7 +9,7 @@ From the [networks/local directory](https://github.com/cosmos/cosmos-sdk/tree/de
### Requirements
- [Install gaia](https://cosmos.network/docs/getting-started/installation.html)
- [Install gaia](./installation.md)
- [Install docker](https://docs.docker.com/engine/installation/)
- [Install docker-compose](https://docs.docker.com/compose/install/)

View File

@ -1,4 +1,4 @@
## Tendermint and Cosmos
# Tendermint and Cosmos
Blockchains can be divided into three conceptual layers:

View File

@ -10,7 +10,7 @@ The ability to tolerate machines failing in arbitrary ways, including becoming m
## Application Blockchain Interface
Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html#abci-overview) for a deep dive into the ABCI.
Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.com/docs/introduction/introduction.html#abci-overview) for a deep dive into the ABCI.
## Understanding the roles of the different layers

View File

@ -169,7 +169,7 @@ gaiacli tx broadcast --node=<node> signedSendTx.json
#### Set up a Validator
Please refer to the [Validator Setup](https://cosmos.network/docs/validators/validator-setup.html) section for a more complete guide on how to set up a validator-candidate.
Please refer to the [Validator Setup](../validators/validator-setup.md) section for a more complete guide on how to set up a validator-candidate.
#### Delegate to a Validator

View File

@ -1,23 +0,0 @@
## Application CLI
**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)**
To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands:
```go
// cmd/simplegovcli/main.go
...
rootCmd.AddCommand(
client.GetCommands(
simplegovcmd.GetCmdQueryProposal("proposals", cdc),
simplegovcmd.GetCmdQueryProposals("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVote("proposals", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplegovcmd.PostCmdPropose(cdc),
simplegovcmd.PostCmdVote(cdc),
)...)
...
```

View File

@ -1,21 +0,0 @@
## Application codec
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
```go
func MakeCodec() *codec.Codec {
var cdc = codec.New()
codec.RegisterCrypto(cdc) // Register crypto.
sdk.RegisterCodec(cdc) // Register Msgs
bank.RegisterCodec(cdc)
simplestake.RegisterCodec(cdc)
simpleGov.RegisterCodec(cdc)
// Register AppAccount
cdc.RegisterInterface((*auth.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil)
return cdc
}
```

View File

@ -1,9 +0,0 @@
## App commands
We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory:
```bash
// At root level of directory
cd cmd
```
`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application.

View File

@ -1,61 +0,0 @@
## Application constructor
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Now, we need to define the constructor for our application.
```go
func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp
```
In this function, we will:
- Create the codec
```go
var cdc = MakeCodec()
```
- Instantiate our application. This includes creating the keys to access each of the substores.
```go
// Create your application object.
var app = &SimpleGovApp{
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"),
}
```
- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it.
```go
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace))
app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace))
```
- Declare the handlers.
```go
app.Router().
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)).
AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper))
```
- Initialize the application.
```go
// Initialize BaseApp.
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
```

View File

@ -1,4 +1,4 @@
## Application initialization
# Application Initialization
In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app.

View File

@ -1,22 +0,0 @@
## Makefile
The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it:
```
// Makefile
build_examples:
ifeq ($(OS),Windows_NT)
...
go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli
else
...
go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli
endif
...
install_examples:
...
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli
```

View File

@ -1,57 +0,0 @@
##### Rest server
**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)**
The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions:
```go
// cmd/simplegovd/main.go
// SimpleGovAppInit initial parameters
var SimpleGovAppInit = server.AppInit{
AppGenState: SimpleGovAppGenState,
AppGenTx: server.SimpleAppGenTx,
}
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
if err != nil {
return
}
return
}
func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewSimpleGovApp(logger, db)
}
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
dapp := app.NewSimpleGovApp(logger, db)
return dapp.ExportAppStateJSON()
}
```
Now, let us define the command for the daemon server within the `main()` function:
```go
// cmd/simplegovd/main.go
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "simplegovd",
Short: "Simple Governance Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit,
server.ConstructAppCreator(newApp, "simplegov"),
server.ConstructAppExporter(exportAppState, "simplegov"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.simplegovd")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}
```

View File

@ -1,55 +0,0 @@
## Application structure
Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory.
```bash
// At root level of directory
cd app
```
We are ready to create our simple governance application!
*Note: You can check the full file (with comments!) [here](link)*
The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed.
Secondly, we need to define the name of our application.
```go
const (
appName = "SimpleGovApp"
)
```
Then, let us define the structure of our application.
```go
// Extended ABCI application
type SimpleGovApp struct {
*bam.BaseApp
cdc *codec.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
capKeySimpleGovStore *sdk.KVStoreKey
// keepers
feeCollectionKeeper auth.FeeCollectionKeeper
bankKeeper bank.Keeper
stakeKeeper simplestake.Keeper
simpleGovKeeper simpleGov.Keeper
// Manage getting and setting accounts
accountMapper auth.AccountMapper
}
```
- Each application builds on top of the `BaseApp` template, hence the pointer.
- `cdc` is the codec used in our application.
- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store.
- Then come the keepers and mappers.
Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use.

View File

@ -0,0 +1,256 @@
# From Module To Application
## Application structure
Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory.
```bash
// At root level of directory
cd app
```
We are ready to create our simple governance application!
*Note: You can check the full file (with comments!) [here](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)*
The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed.
Secondly, we need to define the name of our application.
```go
const (
appName = "SimpleGovApp"
)
```
Then, let us define the structure of our application.
```go
// Extended ABCI application
type SimpleGovApp struct {
*bam.BaseApp
cdc *codec.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
capKeySimpleGovStore *sdk.KVStoreKey
// keepers
feeCollectionKeeper auth.FeeCollectionKeeper
bankKeeper bank.Keeper
stakeKeeper simplestake.Keeper
simpleGovKeeper simpleGov.Keeper
// Manage getting and setting accounts
accountMapper auth.AccountMapper
}
```
- Each application builds on top of the `BaseApp` template, hence the pointer.
- `cdc` is the codec used in our application.
- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store.
- Then come the keepers and mappers.
Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use.
## CLI and Rest server
We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory:
```bash
// At root level of directory
cd cmd
```
`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application.
### Application CLI
**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)**
To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands:
```go
// cmd/simplegovcli/main.go
...
rootCmd.AddCommand(
client.GetCommands(
simplegovcmd.GetCmdQueryProposal("proposals", cdc),
simplegovcmd.GetCmdQueryProposals("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc),
simplegovcmd.GetCmdQueryProposalVote("proposals", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplegovcmd.PostCmdPropose(cdc),
simplegovcmd.PostCmdVote(cdc),
)...)
...
```
### Rest server
**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)**
The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions:
```go
// cmd/simplegovd/main.go
// SimpleGovAppInit initial parameters
var SimpleGovAppInit = server.AppInit{
AppGenState: SimpleGovAppGenState,
AppGenTx: server.SimpleAppGenTx,
}
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
if err != nil {
return
}
return
}
func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewSimpleGovApp(logger, db)
}
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
dapp := app.NewSimpleGovApp(logger, db)
return dapp.ExportAppStateJSON()
}
```
Now, let us define the command for the daemon server within the `main()` function:
```go
// cmd/simplegovd/main.go
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "simplegovd",
Short: "Simple Governance Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit,
server.ConstructAppCreator(newApp, "simplegov"),
server.ConstructAppExporter(exportAppState, "simplegov"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.simplegovd")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}
```
## Makefile
The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it:
```
// Makefile
build_examples:
ifeq ($(OS),Windows_NT)
...
go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli
else
...
go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd
go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli
endif
...
install_examples:
...
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli
```
## Application constructor
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Now, we need to define the constructor for our application.
```go
func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp
```
In this function, we will:
- Create the codec
```go
var cdc = MakeCodec()
```
- Instantiate our application. This includes creating the keys to access each of the substores.
```go
// Create your application object.
var app = &SimpleGovApp{
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"),
}
```
- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it.
```go
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace))
app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace))
```
- Declare the handlers.
```go
app.Router().
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)).
AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper))
```
- Initialize the application.
```go
// Initialize BaseApp.
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
```
## Application codec
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
```go
func MakeCodec() *codec.Codec {
var cdc = codec.New()
codec.RegisterCrypto(cdc) // Register crypto.
sdk.RegisterCodec(cdc) // Register Msgs
bank.RegisterCodec(cdc)
simplestake.RegisterCodec(cdc)
simpleGov.RegisterCodec(cdc)
// Register AppAccount
cdc.RegisterInterface((*auth.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil)
return cdc
}
```

View File

@ -1,19 +0,0 @@
## Cast a vote to an existing proposal
Let's cast a vote on the created proposal:
```bash
simplegovcli vote --proposal-id=1 --option="No"
```
Get the value of the option from your casted vote :
```bash
simplegovcli proposal-vote 1 <your_address>
```
You can also check all the casted votes of a proposal:
```bash
simplegovcli proposals-votes 1
```

View File

@ -6,45 +6,43 @@ Before getting in the bulk of the code, we will start by some introductory conte
## Table of contents:
### Introduction - Prerequsite reading
### Introduction - Prerequisite reading
- [Intro to Tendermint and Cosmos](../../../introduction/tendermint-cosmos.md)
- [Tendermint Core and ABCI](../../../introduction/tendermint.md)
- [Intro to Cosmos-SDK](../../overview.md)
- [Starting your own project](start.md)
- [Intro to Tendermint and Cosmos](/introduction/tendermint-cosmos.md)
- [Tendermint Core and ABCI](/introduction/tendermint.md)
- [Intro to Cosmos-SDK](/sdk/overview.md)
### Setup and design phase
- [Setup](setup.md)
- [Application design](app-design.md)
### [Setup and design](setup-and-design.md)
- [Starting your own project](setup-and-design.md#get-started)
- [Setup](setup-and-design.md#setup)
- [Application design](setup-and-design.md#application-design)
### Implementation of the application
**Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details**
- [Application initialization](app-init.md)
- Simple Governance module
+ [Module initialization](module-init.md)
+ [Types](module-types.md)
+ [Keeper](module-keeper.md)
+ [Handler](module-handler.md)
+ [Wire](module-codec.md)
+ [Errors](module-errors.md)
+ Command-Line Interface and Rest API
* [Command-Line Interface](module-cli.md)
* [Rest API](module-rest.md)
- Bridging it all together
+ [Application structure](app-structure.md)
+ [Application CLI and Rest Server](app-commands.md)
* [Application CLI](app-cli.md)
* [Rest Server](app-rest.md)
+ [Makefile](app-makefile.md)
+ [Application constructor](app-constructor.md)
+ [Application codec](app-codec.md)
- Running the application
+ [Installation](run-install.md)
+ [Submit a proposal](submit-proposal.md)
+ [Cast a vote](cast-vote.md)
- [Simple Governance module](simple-gov-module.md)
+ [Module initialization](simple-gov-module.md#module-initialization)
+ [Types](simple-gov-module.md#types)
+ [Keeper](simple-gov-module.md#keeper)
+ [Handler](simple-gov-module.md#handler)
+ [Wire](simple-gov-module.md#codec)
+ [Errors](simple-gov-module.md#errors)
+ [Command-Line Interface](simple-gov-module.md#command-line-interface)
+ [Rest API](simple-gov-module.md#rest-api)
- [Bridging it all together](bridging-it-all.md)
+ [Application structure](bridging-it-all.md#application-structure)
+ [Application CLI and Rest Server](bridging-it-all.md#cli-and-rest-server)
+ [Makefile](bridging-it-all.md#makefile)
+ [Application constructor](bridging-it-all.md#application-constructor)
+ [Application codec](bridging-it-all.md#application-codec)
- [Running the application](running-the-application.md)
+ [Installation](running-the-application.md#installation)
+ [Submit a proposal](running-the-application.md#submit-a-proposal)
+ [Cast a vote](running-the-application.md#cast-a-vote)
## Useful links

View File

@ -1,33 +0,0 @@
## Command-Line Interface (CLI)
**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)**
Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module.
The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra:
```go
// Declare flags
const(
Flag = "flag"
...
)
// Main command function. One function for each command.
func Command(codec *codec.Codec) *cobra.Command {
// Create the command to return
command := &cobra.Command{
Use: "actual command",
Short: "Short description",
Run: func(cmd *cobra.Command, args []string) error {
// Actual function to run when command is used
},
}
// Add flags to the command
command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>")
return command
}
```

View File

@ -1,13 +0,0 @@
## Codec
**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)**
The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare:
```go
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil)
cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil)
}
```
Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more).

View File

@ -1,7 +0,0 @@
## Errors
**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)**
The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory.
Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result.

View File

@ -1,73 +0,0 @@
## Handler
**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)**
### Constructor and core handlers
Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function.
In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`:
```go
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case SubmitProposalMsg:
return handleSubmitProposalMsg(ctx, k, msg)
case VoteMsg:
return handleVoteMsg(ctx, k, msg)
default:
errMsg := "Unrecognized gov Msg type: " + msg.Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
```
The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures:
```go
func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result
```
Let us take a look at the parameters of this function:
- The context `ctx` to access the stores.
- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case).
- The message `msg` that holds all the information provided by the sender of the transaction.
The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info.
### BeginBlocker and EndBlocker
In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications.
This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether.
`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern):
```go
func NewEndBlocker(k Keeper) sdk.EndBlocker {
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
err := checkProposal(ctx, k)
if err != nil {
panic(err)
}
return
}
}
```
Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md).
For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed:
1. Get the oldest proposal from the `ProposalProcessingQueue`
2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit.
3. Check if proposal is accepted or rejected. Update the proposal status.
4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1.
Let us perform a quick safety analysis on this process.
- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite
- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue.
- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`.

View File

@ -1,31 +0,0 @@
## Module initialization
First, let us go into the module's folder and create a folder for our module.
```bash
cd x/
mkdir simple_governance
cd simple_governance
mkdir -p client/cli client/rest
touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go
```
Let us start by adding the files we will need. Your module's folder should look something like that:
```
x
└─── simple_governance
├─── client
│ ├─── cli
│ │ └─── simple_governance.go
│ └─── rest
│ └─── simple_governance.go
├─── errors.go
├─── handler.go
├─── keeper_keys.go
├─── keeper.go
├─── types.go
└─── codec.go
```
Let us go into the detail of each of these files.

View File

@ -1,96 +0,0 @@
## Keeper
**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)**
### Short intro to keepers
`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos.
As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that:
```go
// in app.go
// instanciate keepers
keeperA = moduleA.newKeeper(app.moduleAStoreKey)
keeperB = moduleB.newKeeper(app.moduleBStoreKey)
// pass instance of keeperA to handler of module B
app.Router().
AddRoute("moduleA", moduleA.NewHandler(keeperA)).
AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance
```
`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper.
### Store for our app
Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them.
- `Proposals` will be indexed by `'proposals'|<proposalID>`.
- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`.
Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`.
These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`.
### Keepers for our app
In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use.
Now, let us try to think about which keeper from **external** modules our module's keepers need access to.
Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler.
With all that in mind, we can define the structure of our `Keeper`:
```go
type Keeper struct {
SimpleGov sdk.StoreKey // Key to our module's store
cdc *codec.Codec // Codec to encore/decode structs
ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance
sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store.
codespace sdk.CodespaceType // Reserves space for error codes
}
```
And the structure of our `KeeperRead`:
```go
type KeeperRead struct {
Keeper
}
```
`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store.
### Functions and Methods
The first function we have to create is the constructor.
```go
func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper
```
This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`.
```go
func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead
```
Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level.
*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers*
Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`.
- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read.
- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored.
- `NewProposalID`: A function to generate a new unique `proposalID`.
- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`.
- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`.
- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period.
The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error.
*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*.

View File

@ -1,32 +0,0 @@
## Rest API
**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)**
The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**.
________________________________________________________
USER INTERFACE <=======> REST SERVER <=======> FULL-NODE
________________________________________________________
It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`.
- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally.
- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally.
Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder.
| Method | URL | Description |
|--------|---------------------------------|-------------------------------------------------------------|
| GET | /proposals | Range query to get all submitted proposals |
| POST | /proposals | Submit a new proposal |
| GET | /proposals/{id} | Returns a proposal given its ID |
| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal |
| POST | /proposals/{id}/votes | Cast a vote on a given proposal |
| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal |
It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it.
Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices.

View File

@ -1,23 +0,0 @@
## Types
**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)**
In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section.
For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in.
Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type:
- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method.
- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message.
- `GetSigners()`: Returns address(es) of the signer(s).
- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction.
- `Get()`: A basic getter, returns some property of the message.
- `String()`: Returns a human-readable version of the message
For our simple governance messages, this means:
- `Type()` will return `"simpleGov"`
- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit
- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative.
- As for other methods, less customization is required. You can check the code to see a standard way of implementing these.

View File

@ -1,18 +0,0 @@
## Installation
Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application:
```bash
go get github.com/<your_username>/cosmos-sdk
cd $GOPATH/src/github.com/<your_username>/cosmos-sdk
make get_vendor_deps
make install
make install_examples
```
Check that the app is correctly installed by typing:
```bash
simplegovcli -h
simplegovd -h
```

View File

@ -0,0 +1,77 @@
# Running The Application
## Installation
Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application:
```bash
go get github.com/<your_username>/cosmos-sdk
cd $GOPATH/src/github.com/<your_username>/cosmos-sdk
make get_vendor_deps
make install
make install_examples
```
Check that the app is correctly installed by typing:
```bash
simplegovcli -h
simplegovd -h
```
## Submit a proposal
Uuse the CLI to create a new proposal:
```bash
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
```
Or, via a json file:
```bash
simplegovcli propose --proposal="path/to/proposal.json"
```
Where proposal.json contains:
```json
{
"title": "Voting Period Update",
"description": "Should we change the proposal voting period to 3 weeks?",
"type": "Text",
"deposit": "300Atoms"
}
```
Get the details of your newly created proposal:
```bash
simplegovcli proposal 1
```
You can also check all the existing open proposals:
```bash
simplegovcli proposals --active=true
```
## Cast a vote
Let's cast a vote on the created proposal:
```bash
simplegovcli vote --proposal-id=1 --option="No"
```
Get the value of the option from your casted vote :
```bash
simplegovcli proposal-vote 1 <your_address>
```
You can also check all the casted votes of a proposal:
```bash
simplegovcli proposals-votes 1
```

View File

@ -1,3 +1,47 @@
# Setup And Design
## Get started
To get started, you just have to follow these simple steps:
1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo
2. Code the modules needed by your application that do not already exist.
3. Create your app directory. In the app main file, import the module you need and instantiate the different stores.
4. Launch your blockchain.
## Setup
### Prerequisites
- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed
- Don't forget to set your `PATH` and `GOPATH`
### Setup work environment
Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and:
```bash
cd $GOPATH/src/github.com/your_username
git clone github.com/your_username/cosmos-sdk
cd cosmos-sdk
```
Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged:
```bash
git remote add upstream github.com/cosmos/cosmos-sdk
git fetch upstream
git rebase upstream/master
```
We will also create a branch dedicated to our module:
```bash
git checkout -b my_new_application
```
We are all set!
## Application design
### Application description

View File

@ -1,32 +0,0 @@
## Setup
### Prerequisites
- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed
- Don't forget to set your `PATH` and `GOPATH`
### Setup work environment
Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and:
```bash
cd $GOPATH/src/github.com/your_username
git clone github.com/your_username/cosmos-sdk
cd cosmos-sdk
```
Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged:
```bash
git remote add upstream github.com/cosmos/cosmos-sdk
git fetch upstream
git rebase upstream/master
```
We will also create a branch dedicated to our module:
```bash
git checkout -b my_new_application
```
We are all set!

View File

@ -0,0 +1,316 @@
# Simple Governance Module
## Module initialization
First, let us go into the module's folder and create a folder for our module.
```bash
cd x/
mkdir simple_governance
cd simple_governance
mkdir -p client/cli client/rest
touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go
```
Let us start by adding the files we will need. Your module's folder should look something like that:
```
x
└─── simple_governance
├─── client
│ ├─── cli
│ │ └─── simple_governance.go
│ └─── rest
│ └─── simple_governance.go
├─── errors.go
├─── handler.go
├─── keeper_keys.go
├─── keeper.go
├─── types.go
└─── codec.go
```
Let us go into the detail of each of these files.
## Types
**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)**
In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section.
For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in.
Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type:
- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method.
- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message.
- `GetSigners()`: Returns address(es) of the signer(s).
- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction.
- `Get()`: A basic getter, returns some property of the message.
- `String()`: Returns a human-readable version of the message
For our simple governance messages, this means:
- `Type()` will return `"simpleGov"`
- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit
- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative.
- As for other methods, less customization is required. You can check the code to see a standard way of implementing these.
## Keeper
**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)**
### Short intro to keepers
`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos.
As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that:
```go
// in app.go
// instanciate keepers
keeperA = moduleA.newKeeper(app.moduleAStoreKey)
keeperB = moduleB.newKeeper(app.moduleBStoreKey)
// pass instance of keeperA to handler of module B
app.Router().
AddRoute("moduleA", moduleA.NewHandler(keeperA)).
AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance
```
`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper.
### Store for our app
Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them.
- `Proposals` will be indexed by `'proposals'|<proposalID>`.
- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`.
Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`.
These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`.
### Keepers for our app
In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use.
Now, let us try to think about which keeper from **external** modules our module's keepers need access to.
Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler.
With all that in mind, we can define the structure of our `Keeper`:
```go
type Keeper struct {
SimpleGov sdk.StoreKey // Key to our module's store
cdc *codec.Codec // Codec to encore/decode structs
ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance
sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store.
codespace sdk.CodespaceType // Reserves space for error codes
}
```
And the structure of our `KeeperRead`:
```go
type KeeperRead struct {
Keeper
}
```
`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store.
### Functions and Methods
The first function we have to create is the constructor.
```go
func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper
```
This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`.
```go
func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead
```
Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level.
*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers*
Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`.
- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read.
- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored.
- `NewProposalID`: A function to generate a new unique `proposalID`.
- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`.
- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`.
- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period.
The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error.
*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*.
## Handler
**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)**
### Constructor and core handlers
Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function.
In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`:
```go
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case SubmitProposalMsg:
return handleSubmitProposalMsg(ctx, k, msg)
case VoteMsg:
return handleVoteMsg(ctx, k, msg)
default:
errMsg := "Unrecognized gov Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
```
The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures:
```go
func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result
```
Let us take a look at the parameters of this function:
- The context `ctx` to access the stores.
- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case).
- The message `msg` that holds all the information provided by the sender of the transaction.
The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info.
### BeginBlocker and EndBlocker
In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications.
This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether.
`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern):
```go
func NewEndBlocker(k Keeper) sdk.EndBlocker {
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
err := checkProposal(ctx, k)
if err != nil {
panic(err)
}
return
}
}
```
Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md).
For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed:
1. Get the oldest proposal from the `ProposalProcessingQueue`
2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit.
3. Check if proposal is accepted or rejected. Update the proposal status.
4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1.
Let us perform a quick safety analysis on this process.
- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite
- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue.
- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`.
## Codec
**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)**
The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare:
```go
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil)
cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil)
}
```
Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more).
## Errors
**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)**
The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory.
Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result.
## Command-Line Interface
**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)**
Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module.
The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra:
```go
// Declare flags
const(
Flag = "flag"
...
)
// Main command function. One function for each command.
func Command(codec *codec.Codec) *cobra.Command {
// Create the command to return
command := &cobra.Command{
Use: "actual command",
Short: "Short description",
Run: func(cmd *cobra.Command, args []string) error {
// Actual function to run when command is used
},
}
// Add flags to the command
command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>")
return command
}
```
## Rest API
**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)**
The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**.
________________________________________________________
USER INTERFACE <=======> REST SERVER <=======> FULL-NODE
________________________________________________________
It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`.
- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally.
- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally.
Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder.
| Method | URL | Description |
|--------|---------------------------------|-------------------------------------------------------------|
| GET | /proposals | Range query to get all submitted proposals |
| POST | /proposals | Submit a new proposal |
| GET | /proposals/{id} | Returns a proposal given its ID |
| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal |
| POST | /proposals/{id}/votes | Cast a vote on a given proposal |
| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal |
It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it.
Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices.

View File

@ -1,10 +0,0 @@
## Starting your own project
To get started, you just have to follow these simple steps:
1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo
2. Code the modules needed by your application that do not already exist.
3. Create your app directory. In the app main file, import the module you need and instantiate the different stores.
4. Launch your blockchain.
Easy as pie! With the introduction over, let us delve into practice and learn how to code a SDK application with an example.

View File

@ -1,36 +0,0 @@
## Submit a proposal
Uuse the CLI to create a new proposal:
```bash
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
```
Or, via a json file:
```bash
simplegovcli propose --proposal="path/to/proposal.json"
```
Where proposal.json contains:
```json
{
"title": "Voting Period Update",
"description": "Should we change the proposal voting period to 3 weeks?",
"type": "Text",
"deposit": "300Atoms"
}
```
Get the details of your newly created proposal:
```bash
simplegovcli proposal 1
```
You can also check all the existing open proposals:
```bash
simplegovcli proposals --active=true
```

View File

@ -216,12 +216,12 @@ We have to solve this simple equation to find the reward R for each validator:
* For the proposer validator:
* The pool obtains `R + R * 5%`: 105 Atoms
* Commission: `105 * 80% * 1%` = 0.84 Atoms
* Validator's reward: `100 * 20% + Commission` = 21.84 Atoms
* Validator's reward: `105 * 20% + Commission` = 21.84 Atoms
* Delegators' rewards: `105 * 80% - Commission` = 83.16 Atoms (each delegator will be able to claim its portion of these rewards in proportion to their stake)
* For each non-proposer validator:
* The pool obtains R: 100 Atoms
* Commission: `100 * 80% * 1%` = 0.8 Atoms
* Validator's reward: `105 * 20% + Commission` = 20.8 Atoms
* Validator's reward: `100 * 20% + Commission` = 20.8 Atoms
* Delegators' rewards: `100 * 80% - Commission` = 79.2 Atoms (each delegator will be able to claim its portion of these rewards in proportion to their stake)
### What are the slashing conditions?

View File

@ -29,7 +29,7 @@ func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.Gene
// initialize and commit the chain
baseApp.InitChain(abci.RequestInitChain{
Validators: []abci.Validator{}, AppStateBytes: stateBytes,
Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes,
})
baseApp.Commit()
@ -72,7 +72,7 @@ func TestGenesis(t *testing.T) {
// initialize the chain with the expected genesis state
baseApp.InitChain(abci.RequestInitChain{
Validators: []abci.Validator{}, AppStateBytes: stateBytes,
Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes,
})
ctx = baseApp.BaseApp.NewContext(true, abci.Header{})

View File

@ -33,7 +33,7 @@ func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error
}
// Initialize the chain
vals := []abci.Validator{}
vals := []abci.ValidatorUpdate{}
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()

View File

@ -10,6 +10,7 @@ import (
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/p2p"
pvm "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
)
@ -97,10 +98,16 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) {
return nil, err
}
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
if err != nil {
return nil, err
}
// create & start tendermint node
tmNode, err := node.NewNode(
cfg,
pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()),
nodeKey,
proxy.NewLocalClientCreator(app),
node.DefaultGenesisDocProviderFunc(cfg),
node.DefaultDBProvider,

View File

@ -1,6 +1,7 @@
package store
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
@ -13,8 +14,8 @@ func newCacheKVStore() CacheKVStore {
return NewCacheKVStore(mem)
}
func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) }
func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) }
func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) }
func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) }
func TestCacheKVStore(t *testing.T) {
mem := dbStoreAdapter{dbm.NewMemDB()}

View File

@ -14,7 +14,7 @@ func First(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) {
}
defer iter.Close()
return cmn.KVPair{iter.Key(), iter.Value()}, true
return cmn.KVPair{Key: iter.Key(), Value: iter.Value()}, true
}
// Gets the last item. `end` is exclusive.
@ -22,7 +22,7 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) {
iter := st.ReverseIterator(end, start)
if !iter.Valid() {
if v := st.Get(start); v != nil {
return cmn.KVPair{cp(start), cp(v)}, true
return cmn.KVPair{Key: cp(start), Value: cp(v)}, true
}
return kv, false
}
@ -36,5 +36,5 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) {
}
}
return cmn.KVPair{iter.Key(), iter.Value()}, true
return cmn.KVPair{Key: iter.Key(), Value: iter.Value()}, true
}

View File

@ -240,7 +240,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
var KVs []KVPair
iterator := sdk.KVStorePrefixIterator(st, subspace)
for ; iterator.Valid(); iterator.Next() {
KVs = append(KVs, KVPair{iterator.Key(), iterator.Value()})
KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()})
}
iterator.Close()
res.Value = cdc.MustMarshalBinary(KVs)
@ -310,7 +310,7 @@ func (iter *iavlIterator) iterateRoutine() {
select {
case <-iter.quitCh:
return true // done with iteration.
case iter.iterCh <- cmn.KVPair{key, value}:
case iter.iterCh <- cmn.KVPair{Key: key, Value: value}:
return false // yay.
}
},

View File

@ -387,12 +387,12 @@ func TestIAVLStoreQuery(t *testing.T) {
ksub := []byte("key")
KVs0 := []KVPair{}
KVs1 := []KVPair{
{k1, v1},
{k2, v2},
{Key: k1, Value: v1},
{Key: k2, Value: v2},
}
KVs2 := []KVPair{
{k1, v3},
{k2, v2},
{Key: k1, Value: v3},
{Key: k2, Value: v2},
}
valExpSubEmpty := cdc.MustMarshalBinary(KVs0)
valExpSub1 := cdc.MustMarshalBinary(KVs1)

View File

@ -5,5 +5,6 @@
"Enable": ["golint", "vet", "ineffassign", "unparam", "unconvert", "misspell"],
"Deadline": "500s",
"Vendor": true,
"Cyclo": 11
}
"Cyclo": 11,
"Exclude": ["/usr/lib/go/src/"]
}

View File

@ -10,7 +10,7 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/types"
)
)
var invalidStrs = []string{
"",

View File

@ -443,7 +443,7 @@ func BenchmarkCoinsAdditionIntersect(b *testing.B) {
}
}
benchmarkSizes := [][]int{[]int{1, 1}, []int{5, 5}, []int{5, 20}, []int{1, 1000}, []int{2, 1000}}
benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}}
for i := 0; i < len(benchmarkSizes); i++ {
sizeA := benchmarkSizes[i][0]
sizeB := benchmarkSizes[i][1]
@ -469,7 +469,7 @@ func BenchmarkCoinsAdditionNoIntersect(b *testing.B) {
}
}
benchmarkSizes := [][]int{[]int{1, 1}, []int{5, 5}, []int{5, 20}, []int{1, 1000}, []int{2, 1000}, []int{1000, 2}}
benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}, {1000, 2}}
for i := 0; i < len(benchmarkSizes); i++ {
sizeA := benchmarkSizes[i][0]
sizeB := benchmarkSizes[i][1]

View File

@ -45,7 +45,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo
c = c.WithIsCheckTx(isCheckTx)
c = c.WithTxBytes(nil)
c = c.WithLogger(logger)
c = c.WithSigningValidators(nil)
c = c.WithVoteInfos(nil)
c = c.WithGasMeter(NewInfiniteGasMeter())
c = c.WithMinimumFees(Coins{})
return c
@ -138,7 +138,7 @@ const (
contextKeyIsCheckTx
contextKeyTxBytes
contextKeyLogger
contextKeySigningValidators
contextKeyVoteInfos
contextKeyGasMeter
contextKeyMinimumFees
)
@ -164,8 +164,8 @@ func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) }
func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) }
func (c Context) SigningValidators() []abci.SigningValidator {
return c.Value(contextKeySigningValidators).([]abci.SigningValidator)
func (c Context) VoteInfos() []abci.VoteInfo {
return c.Value(contextKeyVoteInfos).([]abci.VoteInfo)
}
func (c Context) GasMeter() GasMeter { return c.Value(contextKeyGasMeter).(GasMeter) }
@ -199,8 +199,8 @@ func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contex
func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) }
func (c Context) WithSigningValidators(SigningValidators []abci.SigningValidator) Context {
return c.withValue(contextKeySigningValidators, SigningValidators)
func (c Context) WithVoteInfos(VoteInfos []abci.VoteInfo) Context {
return c.withValue(contextKeyVoteInfos, VoteInfos)
}
func (c Context) WithGasMeter(meter GasMeter) Context { return c.withValue(contextKeyGasMeter, meter) }

View File

@ -151,7 +151,7 @@ func TestContextWithCustom(t *testing.T) {
require.Panics(t, func() { ctx.ChainID() })
require.Panics(t, func() { ctx.TxBytes() })
require.Panics(t, func() { ctx.Logger() })
require.Panics(t, func() { ctx.SigningValidators() })
require.Panics(t, func() { ctx.VoteInfos() })
require.Panics(t, func() { ctx.GasMeter() })
header := abci.Header{}
@ -160,7 +160,7 @@ func TestContextWithCustom(t *testing.T) {
ischeck := true
txbytes := []byte("txbytes")
logger := NewMockLogger()
signvals := []abci.SigningValidator{{}}
voteinfos := []abci.VoteInfo{{}}
meter := types.NewGasMeter(10000)
minFees := types.Coins{types.NewInt64Coin("feeCoin", 1)}
@ -168,7 +168,7 @@ func TestContextWithCustom(t *testing.T) {
WithBlockHeight(height).
WithChainID(chainid).
WithTxBytes(txbytes).
WithSigningValidators(signvals).
WithVoteInfos(voteinfos).
WithGasMeter(meter).
WithMinimumFees(minFees)
@ -178,7 +178,7 @@ func TestContextWithCustom(t *testing.T) {
require.Equal(t, ischeck, ctx.IsCheckTx())
require.Equal(t, txbytes, ctx.TxBytes())
require.Equal(t, logger, ctx.Logger())
require.Equal(t, signvals, ctx.SigningValidators())
require.Equal(t, voteinfos, ctx.VoteInfos())
require.Equal(t, meter, ctx.GasMeter())
require.Equal(t, minFees, types.Coins{types.NewInt64Coin("feeCoin", 1)})
}

View File

@ -2,7 +2,6 @@ package types
import (
"fmt"
"strings"
"github.com/cosmos/cosmos-sdk/codec"
cmn "github.com/tendermint/tendermint/libs/common"
@ -231,13 +230,12 @@ func (err *sdkError) TraceSDK(format string, args ...interface{}) Error {
}
// Implements ABCIError.
// Overrides err.Error.Error().
func (err *sdkError) Error() string {
return fmt.Sprintf(`ERROR:
Codespace: %d
Code: %d
Message: %#v
`, err.codespace, err.code, parseCmnError(err.cmnError.Error()))
`, err.codespace, err.code, err.cmnError.Error())
}
// Implements ABCIError.
@ -258,12 +256,12 @@ func (err *sdkError) Code() CodeType {
// Implements ABCIError.
func (err *sdkError) ABCILog() string {
cdc := codec.New()
parsedErrMsg := parseCmnError(err.cmnError.Error())
errMsg := err.cmnError.Error()
jsonErr := humanReadableError{
Codespace: err.codespace,
Code: err.code,
ABCICode: err.ABCICode(),
Message: parsedErrMsg,
Message: errMsg,
}
bz, er := cdc.MarshalJSON(jsonErr)
if er != nil {
@ -288,13 +286,6 @@ func (err *sdkError) QueryResult() abci.ResponseQuery {
}
}
func parseCmnError(err string) string {
if idx := strings.Index(err, "{"); idx != -1 {
err = err[idx+1 : len(err)-1]
}
return err
}
// nolint
type humanReadableError struct {
Codespace CodespaceType `json:"codespace"`

View File

@ -3,7 +3,6 @@ package types
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
)
// status of a validator
@ -52,7 +51,6 @@ type Validator interface {
// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: tmtypes.TM2PB.PubKey(v.GetConsPubKey()),
Address: v.GetConsPubKey().Address(),
Power: v.GetPower().RoundInt64(),
}

View File

@ -51,4 +51,4 @@ func DefaultChainID() (string, error) {
}
return doc.ChainID, nil
}
}

View File

@ -15,47 +15,36 @@ import (
"github.com/tendermint/tendermint/crypto"
)
// SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both
// SingleInputSendTx tests and runs a single msg send w/ auth, with one input and one output, where both
// accounts already exist.
func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation {
func SingleInputSendTx(mapper auth.AccountMapper) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
fromAcc := simulation.RandomAcc(r, accs)
toAcc := simulation.RandomAcc(r, accs)
// Disallow sending money to yourself
for {
if !fromAcc.PubKey.Equals(toAcc.PubKey) {
break
}
toAcc = simulation.RandomAcc(r, accs)
fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper)
if abort {
return action, nil, nil
}
toAddr := toAcc.Address
initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins()
if len(initFromCoins) == 0 {
return "skipping, no coins at all", nil, nil
err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, nil)
if err != nil {
return "", nil, err
}
event("bank/sendAndVerifyTxSend/ok")
denomIndex := r.Intn(len(initFromCoins))
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
if goErr != nil {
return "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, nil, nil
return action, nil, nil
}
}
// SingleInputSendMsg tests and runs a single msg send, with one input and one output, where both
// accounts already exist.
func SingleInputSendMsg(mapper auth.AccountMapper, bk bank.Keeper) simulation.Operation {
handler := bank.NewHandler(bk)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper)
if abort {
return action, nil, nil
}
action = fmt.Sprintf("%s is sending %s %s to %s",
fromAcc.Address.String(),
amt.String(),
initFromCoins[denomIndex].Denom,
toAddr.String(),
)
coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}}
var msg = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)},
Outputs: []bank.Output{bank.NewOutput(toAddr, coins)},
}
goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey})
if goErr != nil {
return "", nil, goErr
err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler)
if err != nil {
return "", nil, err
}
event("bank/sendAndVerifyMsgSend/ok")
@ -63,8 +52,47 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation
}
}
func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper auth.AccountMapper) (fromAcc simulation.Account, action string, msg bank.MsgSend, abort bool) {
fromAcc = simulation.RandomAcc(r, accs)
toAcc := simulation.RandomAcc(r, accs)
// Disallow sending money to yourself
for {
if !fromAcc.PubKey.Equals(toAcc.PubKey) {
break
}
toAcc = simulation.RandomAcc(r, accs)
}
toAddr := toAcc.Address
initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins()
if len(initFromCoins) == 0 {
return fromAcc, "skipping, no coins at all", msg, true
}
denomIndex := r.Intn(len(initFromCoins))
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
if goErr != nil {
return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, true
}
action = fmt.Sprintf("%s is sending %s %s to %s",
fromAcc.Address.String(),
amt.String(),
initFromCoins[denomIndex].Denom,
toAddr.String(),
)
coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}}
msg = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)},
Outputs: []bank.Output{bank.NewOutput(toAddr, coins)},
}
return
}
// Sends and verifies the transition of a msg send. This fails if there are repeated inputs or outputs
func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey) error {
// pass in handler as nil to handle txs, otherwise handle msgs
func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error {
initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs))
initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs))
AccountNumbers := make([]int64, len(msg.Inputs))
@ -80,14 +108,22 @@ func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountMapper, msg b
acc := mapper.GetAccount(ctx, msg.Outputs[i].Address)
initialOutputAddrCoins[i] = acc.GetCoins()
}
tx := mock.GenTx([]sdk.Msg{msg},
AccountNumbers,
SequenceNumbers,
privkeys...)
res := app.Deliver(tx)
if !res.IsOK() {
// TODO: Do this in a more 'canonical' way
return fmt.Errorf("Deliver failed %v", res)
if handler != nil {
res := handler(ctx, msg)
if !res.IsOK() {
// TODO: Do this in a more 'canonical' way
return fmt.Errorf("handling msg failed %v", res)
}
} else {
tx := mock.GenTx([]sdk.Msg{msg},
AccountNumbers,
SequenceNumbers,
privkeys...)
res := app.Deliver(tx)
if !res.IsOK() {
// TODO: Do this in a more 'canonical' way
return fmt.Errorf("Deliver failed %v", res)
}
}
for i := 0; i < len(msg.Inputs); i++ {

View File

@ -32,14 +32,15 @@ func TestBankWithRandomMessages(t *testing.T) {
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.WeightedOperation{
{1, SimulateSingleInputMsgSend(mapper)},
{1, SingleInputSendTx(mapper)},
{1, SingleInputSendMsg(mapper, bankKeeper)},
},
[]simulation.RandSetup{},
[]simulation.Invariant{
NonnegativeBalanceInvariant(mapper),
TotalCoinsInvariant(mapper, func() sdk.Coins { return mapp.TotalCoinsSupply }),
},
30, 30,
30, 60,
false,
)
}

View File

@ -45,6 +45,7 @@ func TestTallyNoOneVotes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -69,6 +70,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -98,6 +100,7 @@ func TestTallyOnlyValidators51No(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -126,6 +129,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -157,6 +161,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -188,6 +193,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -219,6 +225,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -250,6 +257,7 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -279,6 +287,7 @@ func TestTallyDelgatorOverride(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
stakeHandler(ctx, delegator1Msg)
@ -315,6 +324,7 @@ func TestTallyDelgatorInherit(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
stakeHandler(ctx, delegator1Msg)
@ -349,6 +359,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg)
@ -402,6 +413,8 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg2)
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
proposal.SetStatus(StatusVotingPeriod)
@ -432,6 +445,7 @@ func TestTallyJailedValidator(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg)
@ -443,6 +457,8 @@ func TestTallyJailedValidator(t *testing.T) {
require.True(t, found)
sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
proposal.SetStatus(StatusVotingPeriod)

View File

@ -36,7 +36,8 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)})
validators = make(map[string]mockValidator)
for _, validator := range res.Validators {
validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
str := fmt.Sprintf("%v", validator.PubKey)
validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
}
for i := 0; i < len(setups); i++ {
@ -91,9 +92,9 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
}()
var pastTimes []time.Time
var pastSigningValidators [][]abci.SigningValidator
var pastVoteInfos [][]abci.VoteInfo
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header)
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
// These are operations which have been queued by previous operations
operationQueue := make(map[int][]Operation)
timeOperationQueue := []FutureOperation{}
@ -122,7 +123,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
for i := 0; i < numBlocks && !stopEarly; i++ {
// Log the header time for future lookup
pastTimes = append(pastTimes, header.Time)
pastSigningValidators = append(pastSigningValidators, request.LastCommitInfo.Validators)
pastVoteInfos = append(pastVoteInfos, request.LastCommitInfo.Votes)
// Run the BeginBlock handler
app.BeginBlock(request)
@ -157,7 +158,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
}
// Generate a random RequestBeginBlock with the current validator set for the next block
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header)
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
// Update the validator set
validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
@ -316,11 +317,11 @@ func getKeys(validators map[string]mockValidator) []string {
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
// nolint: unparam
func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
pastTimes []time.Time, pastSigningValidators [][]abci.SigningValidator, event func(string), header abci.Header) abci.RequestBeginBlock {
pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock {
if len(validators) == 0 {
return abci.RequestBeginBlock{Header: header}
}
signingValidators := make([]abci.SigningValidator, len(validators))
voteInfos := make([]abci.VoteInfo, len(validators))
i := 0
for _, key := range getKeys(validators) {
mVal := validators[key]
@ -341,8 +342,14 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
} else {
event("beginblock/signing/missed")
}
signingValidators[i] = abci.SigningValidator{
Validator: mVal.val,
pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey)
if err != nil {
panic(err)
}
voteInfos[i] = abci.VoteInfo{
Validator: abci.Validator{
Address: pubkey.Address(),
},
SignedLastBlock: signed,
}
i++
@ -354,11 +361,11 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
for r.Float64() < evidenceFraction {
height := header.Height
time := header.Time
vals := signingValidators
vals := voteInfos
if r.Float64() < pastEvidenceFraction {
height = int64(r.Intn(int(header.Height)))
time = pastTimes[height]
vals = pastSigningValidators[height]
vals = pastVoteInfos[height]
}
validator := vals[r.Intn(len(vals))].Validator
var totalVotingPower int64
@ -378,7 +385,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
return abci.RequestBeginBlock{
Header: header,
LastCommitInfo: abci.LastCommitInfo{
Validators: signingValidators,
Votes: voteInfos,
},
ByzantineValidators: evidence,
}
@ -386,25 +393,26 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
// updateValidators mimicks Tendermint's update logic
// nolint: unparam
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator {
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {
for _, update := range updates {
str := fmt.Sprintf("%v", update.PubKey)
switch {
case update.Power == 0:
if _, ok := current[string(update.PubKey.Data)]; !ok {
if _, ok := current[str]; !ok {
tb.Fatalf("tried to delete a nonexistent validator")
}
event("endblock/validatorupdates/kicked")
delete(current, string(update.PubKey.Data))
delete(current, str)
default:
// Does validator already exist?
if mVal, ok := current[string(update.PubKey.Data)]; ok {
if mVal, ok := current[str]; ok {
mVal.val = update
event("endblock/validatorupdates/updated")
} else {
// Set this new validator
current[string(update.PubKey.Data)] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
event("endblock/validatorupdates/added")
}
}

View File

@ -45,7 +45,7 @@ type (
}
mockValidator struct {
val abci.Validator
val abci.ValidatorUpdate
livenessState int
}

View File

@ -45,6 +45,9 @@ func TestJailedValidatorDelegations(t *testing.T) {
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
// end block
stake.EndBlocker(ctx, stakeKeeper)
// set dummy signing info
newInfo := ValidatorSigningInfo{
StartHeight: int64(0),

View File

@ -138,7 +138,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
}
// AddValidators adds the validators to the keepers validator addr to pubkey mapping.
func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.Validator) {
func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.ValidatorUpdate) {
for i := 0; i < len(vals); i++ {
val := vals[i]
pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey)

View File

@ -85,13 +85,16 @@ func TestSlashingPeriodCap(t *testing.T) {
// double sign less than max age
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
// should be jailed
require.True(t, sk.Validator(ctx, addr).GetJailed())
// end block
stake.EndBlocker(ctx, sk)
// update block height
ctx = ctx.WithBlockHeight(int64(1))
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be reduced
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -100,10 +103,14 @@ func TestSlashingPeriodCap(t *testing.T) {
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
// should be jailed
require.True(t, sk.Validator(ctx, addr).GetJailed())
// end block
stake.EndBlocker(ctx, sk)
// update block height
ctx = ctx.WithBlockHeight(int64(2))
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be equal, no more should have been slashed
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -114,6 +121,8 @@ func TestSlashingPeriodCap(t *testing.T) {
require.True(t, sk.Validator(ctx, addr).GetJailed())
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be reduced
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -180,6 +189,9 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
// end block
stake.EndBlocker(ctx, sk)
// validator should have been jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
@ -193,6 +205,9 @@ func TestHandleAbsentValidator(t *testing.T) {
got = slh(ctx, NewMsgUnjail(addr))
require.True(t, got.IsOK())
// end block
stake.EndBlocker(ctx, sk)
// validator should be rebonded now
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
@ -222,12 +237,19 @@ func TestHandleAbsentValidator(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should be jailed again after 500 unsigned blocks
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
for ; height <= nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}
@ -296,6 +318,9 @@ func TestHandleAlreadyJailed(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should have been jailed and slashed
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())

View File

@ -20,9 +20,8 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags
// Iterate over all the validators which *should* have signed this block
// Store whether or not they have actually signed it and slash/unbond any
// which have missed too many blocks in a row (downtime slashing)
for _, signingValidator := range req.LastCommitInfo.GetValidators() {
present := signingValidator.SignedLastBlock
sk.handleValidatorSignature(ctx, signingValidator.Validator.Address, signingValidator.Validator.Power, present)
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
sk.handleValidatorSignature(ctx, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock)
}
// Iterate through any newly discovered evidence of infraction

View File

@ -32,7 +32,7 @@ func TestBeginBlocker(t *testing.T) {
// mark the validator as having signed
req := abci.RequestBeginBlock{
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Votes: []abci.VoteInfo{{
Validator: val,
SignedLastBlock: true,
}},
@ -54,7 +54,7 @@ func TestBeginBlocker(t *testing.T) {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Votes: []abci.VoteInfo{{
Validator: val,
SignedLastBlock: true,
}},
@ -68,7 +68,7 @@ func TestBeginBlocker(t *testing.T) {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Votes: []abci.VoteInfo{{
Validator: val,
SignedLastBlock: false,
}},
@ -77,6 +77,9 @@ func TestBeginBlocker(t *testing.T) {
BeginBlocker(ctx, req, keeper)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should be jailed
validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk))
require.True(t, found)

View File

@ -15,9 +15,9 @@ import (
// addition, it also sets any delegations found in data. Finally, it updates
// the bonded validators.
// Returns final validator set after applying all declaration and delegations
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.Validator, err error) {
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
keeper.SetPool(ctx, data.Pool)
keeper.SetNewParams(ctx, data.Params)
keeper.SetParams(ctx, data.Params)
keeper.InitIntraTxCounter(ctx)
for i, validator := range data.Validators {
@ -31,26 +31,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator)
}
// Manually set indexes for the first time
// Manually set indices for the first time
keeper.SetValidatorByConsAddr(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
if validator.Status == sdk.Bonded {
keeper.SetValidatorBondedIndex(ctx, validator)
}
}
for _, bond := range data.Bonds {
keeper.SetDelegation(ctx, bond)
}
keeper.UpdateBondedValidatorsFull(ctx)
vals := keeper.GetValidatorsBonded(ctx)
res = make([]abci.Validator, len(vals))
for i, val := range vals {
res[i] = sdk.ABCIValidator(val)
}
res = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
return
}

View File

@ -59,9 +59,9 @@ func TestInitGenesis(t *testing.T) {
require.Equal(t, sdk.Bonded, resVal.Status)
require.Equal(t, int16(1), resVal.BondIntraTxCounter)
abcivals := make([]abci.Validator, len(vals))
abcivals := make([]abci.ValidatorUpdate, len(vals))
for i, val := range validators {
abcivals[i] = sdk.ABCIValidator(val)
abcivals[i] = val.ABCIValidatorUpdate()
}
require.Equal(t, abcivals, vals)
@ -98,9 +98,9 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) {
vals, err := InitGenesis(ctx, keeper, genesisState)
require.NoError(t, err)
abcivals := make([]abci.Validator, 100)
abcivals := make([]abci.ValidatorUpdate, 100)
for i, val := range validators[:100] {
abcivals[i] = sdk.ABCIValidator(val)
abcivals[i] = val.ABCIValidatorUpdate()
}
require.Equal(t, abcivals, vals)

View File

@ -36,7 +36,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
}
// Called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) {
pool := k.GetPool(ctx)
// Process provision inflation
@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
k.SetIntraTxCounter(ctx, 0)
// calculate validator set changes
ValidatorUpdates = k.GetValidTendermintUpdates(ctx)
ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx)
return
}
@ -82,7 +82,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
msg.Commission.Rate, msg.Commission.MaxChangeRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
)
validator, err := validator.SetInitialCommission(commission)
if err != nil {
return err.Result()
@ -90,6 +89,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
k.SetValidator(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
k.SetNewValidatorByPowerIndex(ctx, validator)
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
@ -130,13 +130,13 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
validator.Description = description
if msg.CommissionRate != nil {
if err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate); err != nil {
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
if err != nil {
return err.Result()
}
validator.Commission = commission
}
// We don't need to run through all the power update logic within k.UpdateValidator
// We just need to override the entry in state, since only the description has changed.
k.SetValidator(ctx, validator)
tags := sdk.NewTags(

View File

@ -63,6 +63,10 @@ func TestValidatorByPowerIndex(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// verify the self-delegation exists
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.True(t, found)
@ -83,14 +87,20 @@ func TestValidatorByPowerIndex(t *testing.T) {
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// must end-block
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// slash and jail the first validator
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1))
keeper.Jail(ctx, consAddr0)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
keeper.Unjail(ctx, consAddr0)
// the old power record should have been deleted as the power changed
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
@ -121,6 +131,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
EndBlocker(ctx, keeper)
// verify that by power key nolonger exists
_, found = keeper.GetValidator(ctx, validatorAddr)
require.False(t, found)
@ -136,8 +148,10 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper)
require.True(t, got.IsOK(), "%v", got)
validator, found := keeper.GetValidator(ctx, addr1)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, addr1)
require.True(t, found)
assert.Equal(t, sdk.Bonded, validator.Status)
assert.Equal(t, addr1, validator.OperatorAddr)
@ -160,6 +174,11 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper)
require.True(t, got.IsOK(), "%v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found = keeper.GetValidator(ctx, addr2)
require.True(t, found)
@ -180,6 +199,11 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.True(t, got.IsOK(), "%v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
@ -211,6 +235,10 @@ func TestLegacyValidatorDelegations(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// verify the validator exists and has the correct attributes
validator, found := keeper.GetValidator(ctx, valAddr)
require.True(t, found)
@ -302,6 +330,9 @@ func TestIncrementsMsgDelegate(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status)
@ -384,6 +415,9 @@ func TestIncrementsMsgUnbond(t *testing.T) {
amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
@ -533,11 +567,8 @@ func TestMultipleMsgDelegate(t *testing.T) {
// unbond them all
for i, delegatorAddr := range delegatorAddrs {
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(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
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
@ -564,11 +595,8 @@ func TestJailValidator(t *testing.T) {
// unbond the validators bond portion
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(sdk.AccAddress(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")
require.True(t, got.IsOK(), "expected no error: %v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
@ -750,16 +778,22 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
// unbond the valdator-2
@ -767,6 +801,9 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// because there are extra validators waiting to get in, the queued
// validator (aka. validator-1) should make it into the bonded group, thus
// the total number of validators should stay the same
@ -777,142 +814,6 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
require.Equal(t, sdk.Bonded, val1.Status, "%v", val1)
}
func TestJoiningAsCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, the val-2 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
}
func TestJoiningToCreateFirstCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 60)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, validator-1 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr1.Bytes(), cliffVal)
}
func TestCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1 := sdk.ValAddress(keep.Addrs[0])
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
validatorAddr3 := sdk.ValAddress(keep.Addrs[2])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, validator-2 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
// add the third validator, which should not make it to being bonded,
// so the cliff validator should not change because nobody has been kicked out
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
// unbond valdator-2
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
vals := keeper.GetValidatorsBonded(ctx)
require.Equal(t, 2, len(vals))
// now the validator set should be updated,
// where val-3 enters the validator set on the cliff
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr3.Bytes(), cliffVal)
// unbond valdator-1
msgBeginUnbonding = NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr1), validatorAddr1, sdk.NewDec(50))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
// get bonded validators - should just be one
vals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(vals))
// cliff now should be empty
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
}
func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
@ -931,6 +832,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDelegate")
// apply Tendermint updates
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
// a block passes
ctx = ctx.WithBlockHeight(1)
@ -949,6 +854,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
require.True(t, found)
require.Equal(t, sdk.NewDec(6), delegation.Shares)
// must apply validator updates
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
// slash the validator by half
keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1))
@ -991,6 +900,9 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
require.True(t, found)
require.Equal(t, sdk.NewDec(3), delegation.Shares)
// end blocker
EndBlocker(ctx, keeper)
// validator power should have been reduced to zero
// ergo validator should have been removed from the store
_, found = keeper.GetValidator(ctx, valA)

View File

@ -41,9 +41,3 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
The transient store persists between transations but not between blocks
## Tendermint Updates
- Prefix Key Space: TendermintUpdatesTKey
- Key/Sort: Validator Operator Address
- Value: Tendermint ABCI Validator
- Contains: Validators are queued to affect the consensus validation set in Tendermint
- Used For: Informing Tendermint of the validator set updates

View File

@ -265,18 +265,13 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
}
}
pool := k.GetPool(ctx)
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount)
validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt.Amount)
// Update delegation
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
return newShares, nil
}
// unbond the the delegation return
@ -313,8 +308,9 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
// if the delegation is the operator of the validator then
// trigger a jail validator
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && validator.Jailed == false {
validator.Jailed = true
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && !validator.Jailed {
k.jailValidator(ctx, validator)
validator = k.mustGetValidator(ctx, validator.OperatorAddr)
}
k.RemoveDelegation(ctx, delegation)
@ -325,14 +321,10 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
}
// remove the coins from the validator
pool := k.GetPool(ctx)
validator, pool, amount = validator.RemoveDelShares(pool, shares)
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
k.SetPool(ctx, pool)
// update then remove validator if necessary
validator = k.UpdateValidator(ctx, validator)
if validator.DelegatorShares.IsZero() {
if validator.DelegatorShares.IsZero() && validator.Status != sdk.Bonded {
// if bonded, we must remove in EndBlocker instead
k.RemoveValidator(ctx, validator.OperatorAddr)
}
@ -368,7 +360,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd
}
}
// complete unbonding an unbonding record
// begin unbonding an unbonding record
func (k Keeper) BeginUnbonding(ctx sdk.Context,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {

View File

@ -25,9 +25,9 @@ func TestDelegation(t *testing.T) {
}
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])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
// first add a validators[0] to delegate too
@ -184,7 +184,7 @@ func TestUnbondDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
@ -226,7 +226,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -236,10 +236,11 @@ func TestUndelegateSelfDelegation(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -252,6 +253,10 @@ func TestUndelegateSelfDelegation(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
@ -269,7 +274,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -279,10 +284,11 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -303,6 +309,10 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -340,7 +350,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -351,10 +361,11 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -374,6 +385,10 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -506,7 +521,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -520,14 +535,16 @@ func TestRedelegateSelfDelegation(t *testing.T) {
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(10))
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -539,6 +556,10 @@ func TestRedelegateSelfDelegation(t *testing.T) {
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
@ -552,11 +573,12 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator.BondIntraTxCounter = 1
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -567,10 +589,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -581,10 +604,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2.BondIntraTxCounter = 2
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
header := ctx.BlockHeader()
blockHeight := int64(10)
@ -597,6 +621,10 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -634,7 +662,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -645,10 +673,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
validator.BondIntraTxCounter = 1
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -659,10 +689,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2.BondIntraTxCounter = 2
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
header := ctx.BlockHeader()
blockHeight := int64(10)
@ -675,6 +707,10 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)

View File

@ -64,25 +64,9 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
return
}
// Need a distinct function because setParams depends on an existing previous
// record of params to exist (to check if maxValidators has changed) - and we
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
// first params set it will panic.
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
// set the params
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
exParams := k.GetParams(ctx)
// if max validator count changes, must recalculate validator set
if exParams.MaxValidators != params.MaxValidators {
k.UpdateBondedValidatorsFull(ctx)
}
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}

View File

@ -18,18 +18,13 @@ var (
ValidatorsByConsAddrKey = []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
IntraTxCounterKey = []byte{0x08} // key for intra-block tx index
DelegationKey = []byte{0x09} // key for a delegation
UnbondingDelegationKey = []byte{0x0A} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x0B} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0C} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0D} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by destination validator operator
// Keys for store prefixes (transient)
TendermintUpdatesTKey = []byte{0x00} // prefix for each key to a validator which is being updated
IntraTxCounterKey = []byte{0x06} // key for intra-block tx index
DelegationKey = []byte{0x07} // key for a delegation
UnbondingDelegationKey = []byte{0x08} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x09} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0A} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@ -46,12 +41,6 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte {
return append(ValidatorsByConsAddrKey, addr.Bytes()...)
}
// gets the key for the current validator group
// VALUE: none (key rearrangement with GetValKeyFromValBondedIndexKey)
func GetValidatorsBondedIndexKey(operatorAddr sdk.ValAddress) []byte {
return append(ValidatorsBondedIndexKey, operatorAddr.Bytes()...)
}
// Get the validator operator address from ValBondedIndexKey
func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte {
return IndexKey[1:] // remove prefix bytes
@ -66,6 +55,11 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []
return getValidatorPowerRank(validator, pool)
}
// get the bonded validator index key for an operator address
func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte {
return append(ValidatorsBondedIndexKey, operator...)
}
// get the power ranking of a validator
// NOTE the larger values are of higher value
// nolint: unparam
@ -74,34 +68,19 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
potentialPower := validator.Tokens
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
jailedBytes := make([]byte, 1)
if validator.Jailed {
jailedBytes[0] = byte(0x00)
} else {
jailedBytes[0] = byte(0x01)
}
// 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(append(append(append(
return append(append(append(
ValidatorsByPowerIndexKey,
jailedBytes...),
powerBytes...),
heightBytes...),
counterBytes...)
}
// get the key for the accumulated update validators
// VALUE: abci.Validator
// note records using these keys should never persist between blocks
func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte {
return append(TendermintUpdatesTKey, operatorAddr.Bytes()...)
}
//______________________________________________________________________________
// gets the key for delegator bond with validator

View File

@ -95,20 +95,18 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
}
}
// Cannot decrease balance below zero
// cannot decrease balance below zero
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
// burn validator's tokens
// burn validator's tokens and update the validator
validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn)
pool := k.GetPool(ctx)
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
k.SetPool(ctx, pool)
// update the validator, possibly kicking it out
validator = k.UpdateValidator(ctx, validator)
// remove validator if it has no more tokens
if validator.Tokens.IsZero() {
if validator.Tokens.IsZero() && validator.Status != sdk.Bonded {
// if bonded, we must remove in ApplyAndReturnValidatorSetUpdates instead
k.RemoveValidator(ctx, validator.OperatorAddr)
}
@ -123,7 +121,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
// jail a validator
func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
k.setJailed(ctx, consAddr, true)
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
k.jailValidator(ctx, validator)
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("validator %s jailed", consAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
@ -132,24 +131,14 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
// unjail a validator
func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
k.setJailed(ctx, consAddr, false)
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
k.unjailValidator(ctx, validator)
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("validator %s unjailed", consAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
return
}
// set the jailed flag on a validator
func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) {
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
if !found {
panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed))
}
validator.Jailed = isJailed
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
return
}
// slash an unbonding delegation and update the pool
// return the amount that would have been slashed assuming
// the unbonding delegation had enough stake to slash

View File

@ -26,8 +26,10 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
for i := 0; i < numVals; i++ {
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt))
validator.BondIntraTxCounter = int16(i)
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(amt))
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
keeper.SetValidatorByConsAddr(ctx, validator)
}
pool = keeper.GetPool(ctx)
@ -161,6 +163,10 @@ func TestSlashRedelegation(t *testing.T) {
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.True(t, found)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// initialbalance unchanged
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
@ -201,6 +207,11 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) {
require.True(t, found)
newPool := keeper.GetPool(ctx)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates)
validator = keeper.mustGetValidator(ctx, validator.OperatorAddr)
// power decreased
require.Equal(t, sdk.NewDec(5), validator.GetPower())
// pool bonded shares decreased
@ -232,6 +243,10 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
require.True(t, found)
keeper.Slash(ctx, consAddr, 10, 10, fraction)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// read updating unbonding delegation
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
require.True(t, found)
@ -301,6 +316,8 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
newPool = keeper.GetPool(ctx)
// just 1 bonded token burned again since that's all the validator now has
require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// power decreased by 1 again, validator is out of stake
// ergo validator should have been removed from the store
@ -402,6 +419,8 @@ func TestSlashWithRedelegation(t *testing.T) {
newPool = keeper.GetPool(ctx)
// four more bonded tokens burned
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// validator decreased to zero power, should have been removed from the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)

View File

@ -111,7 +111,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
ck := bank.NewBaseKeeper(accountMapper)
keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace)
keeper.SetPool(ctx, types.InitialPool())
keeper.SetNewParams(ctx, types.DefaultParams())
keeper.SetParams(ctx, types.DefaultParams())
keeper.InitIntraTxCounter(ctx)
// fill all the addresses with some coins, set the loose pool tokens simultaneously
@ -202,5 +202,22 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
// 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
return store.Has(power)
}
func testingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator {
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validator.OperatorAddr)
if !found {
panic("validator expected but not found")
}
return validator
}
func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool {
store := ctx.KVStore(k.storeKey)
return store.Has(power)
}

View File

@ -0,0 +1,268 @@
package keeper
import (
"bytes"
"fmt"
"sort"
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// Apply and return accumulated updates to the bonded validator set
//
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
// at the previous block height or were removed from the validator set entirely
// are returned to Tendermint.
func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
// retrieve last validator set
last := k.retrieveLastValidatorSet(ctx)
// iterate over validators, highest power to lowest
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
count := 0
for ; iterator.Valid() && count < int(maxValidators); iterator.Next() {
// fetch the validator
operator := sdk.ValAddress(iterator.Value())
validator := k.mustGetValidator(ctx, operator)
if validator.Jailed {
panic("should never retrieve a jailed validator from the power store")
}
// if we get to a zero-power validator (which we don't bond),
// there are no more possible bonded validators
// note: we must check the ABCI power, since we round before sending to Tendermint
if validator.Tokens.RoundInt64() == int64(0) {
break
}
// apply the appropriate state change if necessary
switch validator.Status {
case sdk.Unbonded:
validator = k.unbondedToBonded(ctx, validator)
case sdk.Unbonding:
validator = k.unbondingToBonded(ctx, validator)
case sdk.Bonded:
// no state change
default:
panic("unexpected validator status")
}
// fetch the old power bytes
var operatorBytes [sdk.AddrLen]byte
copy(operatorBytes[:], operator[:])
oldPowerBytes, found := last[operatorBytes]
// calculate the new power bytes
newPowerBytes := validator.ABCIValidatorPowerBytes(k.cdc)
// update the validator set if power has changed
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
updates = append(updates, validator.ABCIValidatorUpdate())
}
// validator still in the validator set, so delete from the copy
delete(last, operatorBytes)
// set the bonded validator index
store.Set(GetBondedValidatorIndexKey(operator), newPowerBytes)
// keep count
count++
}
// sort the no-longer-bonded validators
noLongerBonded := k.sortNoLongerBonded(last)
// iterate through the sorted no-longer-bonded validators
for _, operator := range noLongerBonded {
// fetch the validator
validator := k.mustGetValidator(ctx, sdk.ValAddress(operator))
// bonded to unbonding
k.bondedToUnbonding(ctx, validator)
// remove validator if it has no more tokens
if validator.Tokens.IsZero() {
k.RemoveValidator(ctx, validator.OperatorAddr)
}
// delete from the bonded validator index
store.Delete(GetBondedValidatorIndexKey(operator))
// update the validator set
updates = append(updates, validator.ABCIValidatorUpdateZero())
}
return updates
}
// Validator state transitions
func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Bonded {
panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator))
}
return k.beginUnbondingValidator(ctx, validator)
}
func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonding {
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
}
return k.bondValidator(ctx, validator)
}
func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonded {
panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator))
}
return k.bondValidator(ctx, validator)
}
func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonded {
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
}
return k.completeUnbondingValidator(ctx, validator)
}
// send a validator to jail
func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) {
if validator.Jailed {
panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator))
}
pool := k.GetPool(ctx)
validator.Jailed = true
k.SetValidator(ctx, validator)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
}
// remove a validator from jail
func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) {
if !validator.Jailed {
panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator))
}
pool := k.GetPool(ctx)
validator.Jailed = false
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
}
// perform all the store operations for when a validator status becomes bonded
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator.BondHeight = ctx.BlockHeight()
// 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
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
// call the bond hook if present
if k.hooks != nil {
k.hooks.OnValidatorBonded(ctx, validator.ConsAddress())
}
return validator
}
// perform all the store operations for when a validator status begins unbonding
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
params := k.GetParams(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
// sanity check
if validator.Status != sdk.Bonded {
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
k.SetPool(ctx, pool)
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
validator.UnbondingHeight = ctx.BlockHeader().Height
// save the now unbonded validator record
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
// call the unbond hook if present
if k.hooks != nil {
k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress())
}
return validator
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
k.SetPool(ctx, pool)
k.SetValidator(ctx, validator)
return validator
}
// map of operator addresses to serialized power
type validatorsByAddr map[[sdk.AddrLen]byte][]byte
// retrieve the last validator set
func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr {
last := make(validatorsByAddr)
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
for ; iterator.Valid(); iterator.Next() {
var operator [sdk.AddrLen]byte
copy(operator[:], iterator.Key()[1:])
powerBytes := iterator.Value()
last[operator] = make([]byte, len(powerBytes))
copy(last[operator][:], powerBytes[:])
}
return last
}
// given a map of remaining validators to previous bonded power
// returns the list of validators to be unbonded, sorted by operator address
func (k Keeper) sortNoLongerBonded(last validatorsByAddr) [][]byte {
// sort the map keys for determinism
noLongerBonded := make([][]byte, len(last))
index := 0
for operatorBytes := range last {
operator := make([]byte, sdk.AddrLen)
copy(operator[:], operatorBytes[:])
noLongerBonded[index] = operator
index++
}
// sorted by address - order doesn't matter
sort.SliceStable(noLongerBonded, func(i, j int) bool {
return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1
})
return noLongerBonded
}

View File

@ -1,12 +1,9 @@
package keeper
import (
"bytes"
"container/list"
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
@ -57,6 +54,14 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty
return validator, true
}
func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Validator {
validator, found := k.GetValidator(ctx, addr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %X\n", addr))
}
return validator
}
// get a single validator by consensus address
func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
@ -67,6 +72,16 @@ func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress
return k.GetValidator(ctx, opAddr)
}
func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) types.Validator {
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
if !found {
panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr))
}
return validator
}
//___________________________________________________________________________
// set the main record holding validator details
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
@ -83,23 +98,110 @@ func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validato
// validator index
func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
// jailed validators are not kept in the power index
if validator.Jailed {
return
}
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr)
}
// validator index
func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) {
func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{})
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
}
// used in testing
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
// validator index
func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
return store.Get(power) != nil
pool := k.GetPool(ctx)
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr)
}
// Get the set of all validators with no limits, used during genesis dump
//___________________________________________________________________________
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator,
tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, addedShares = validator.AddTokensFromDel(pool, tokensToAdd)
// increment the intra-tx counter
// in case of a conflict, the validator which least recently changed power takes precedence
counter := k.GetIntraTxCounter(ctx)
validator.BondIntraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator, addedShares
}
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator,
sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Dec) {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, removedTokens = validator.RemoveDelShares(pool, sharesToRemove)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator, removedTokens
}
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool = validator.RemoveTokens(pool, tokensToRemove)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator
}
// UpdateValidatorCommission attempts to update a validator's commission rate.
// An error is returned if the new commission rate is invalid.
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) (types.Commission, sdk.Error) {
commission := validator.Commission
blockTime := ctx.BlockHeader().Time
if err := commission.ValidateNewRate(newRate, blockTime); err != nil {
return commission, err
}
commission.Rate = newRate
commission.UpdateTime = blockTime
return commission, nil
}
// remove the validator record and associated indexes
// except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
// 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(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address())))
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
}
//___________________________________________________________________________
// get groups of validators
// 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)
@ -131,8 +233,6 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [
return validators[:i] // trim if the array length < maxRetrieve
}
//___________________________________________________________________________
// get the group of the bonded validators
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
@ -152,8 +252,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
}
address := GetAddressFromValBondedIndexKey(iterator.Key())
validator, found := k.GetValidator(ctx, address)
ensureValidatorFound(found, address)
validator := k.mustGetValidator(ctx, address)
validators[i] = validator
i++
@ -162,9 +261,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat
}
// get the group of bonded validators sorted by power-rank
//
// TODO: Rename to GetBondedValidatorsByPower or GetValidatorsByPower(ctx, status)
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
validators := make([]types.Validator, maxValidators)
@ -175,8 +272,7 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
i := 0
for ; iterator.Valid() && i < int(maxValidators); iterator.Next() {
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
ensureValidatorFound(found, address)
validator := k.mustGetValidator(ctx, address)
if validator.Status == sdk.Bonded {
validators[i] = validator
@ -185,551 +281,3 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
}
return validators[:i] // trim
}
//_________________________________________________________________________
// Accumulated updates to the active/bonded validator set for tendermint
// get the most recently updated validators
//
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
// at the previous block height or were removed from the validator set entirely
// are returned to Tendermint.
func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
tstore := ctx.TransientStore(k.storeTKey)
iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var abciVal abci.Validator
abciValBytes := iterator.Value()
k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal)
val, found := k.GetValidator(ctx, abciVal.GetAddress())
if found {
// The validator is new or already exists in the store and must adhere to
// Tendermint invariants.
prevBonded := val.BondHeight < ctx.BlockHeight() && val.BondHeight > val.UnbondingHeight
zeroPower := val.GetPower().Equal(sdk.ZeroDec())
if !zeroPower || zeroPower && prevBonded {
updates = append(updates, abciVal)
}
} else {
// Add the ABCI validator in such a case where the validator was removed
// from the store as it must have existed before.
updates = append(updates, abciVal)
}
}
return
}
//___________________________________________________________________________
// Perform all the necessary steps for when a validator changes its power. This
// function updates all validator stores as well as tendermint update store.
// It may kick out validators if a new validator is entering the bonded validator
// group.
//
// TODO: Remove above nolint, function needs to be simplified!
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
tstore := ctx.TransientStore(k.storeTKey)
pool := k.GetPool(ctx)
oldValidator, oldFound := k.GetValidator(ctx, validator.OperatorAddr)
validator = k.updateForJailing(ctx, oldFound, oldValidator, validator)
powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator)
validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator)
valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool)
cliffPower := k.GetCliffValidatorPower(ctx)
cliffValExists := (cliffPower != nil)
var valPowerLTcliffPower bool
if cliffValExists {
valPowerLTcliffPower = (bytes.Compare(valPower, cliffPower) == -1)
}
switch {
// if the validator is already bonded and the power is increasing, we need
// perform the following:
// a) update Tendermint
// b) check if the cliff validator needs to be updated
case powerIncreasing && !validator.Jailed &&
(oldFound && oldValidator.Status == sdk.Bonded):
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
if cliffValExists {
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
if bytes.Equal(cliffAddr, validator.OperatorAddr) {
k.updateCliffValidator(ctx, validator)
}
}
// if is a new validator and the new power is less than the cliff validator
case cliffValExists && !oldFound && valPowerLTcliffPower:
// skip to completion
// if was unbonded and the new power is less than the cliff validator
case cliffValExists &&
(oldFound && oldValidator.Status == sdk.Unbonded) &&
valPowerLTcliffPower: //(valPower < cliffPower
// skip to completion
default:
// default case - validator was either:
// a) not-bonded and now has power-rank greater than cliff validator
// b) bonded and now has decreased in power
// update the validator set for this validator
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
if updated {
// the validator has changed bonding status
validator = updatedVal
break
}
// if decreased in power but still bonded, update Tendermint validator
if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) {
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
}
}
k.SetValidator(ctx, validator)
return validator
}
// updateCliffValidator determines if the current cliff validator needs to be
// updated or swapped. If the provided affected validator is the current cliff
// validator before it's power was increased, either the cliff power key will
// be updated or if it's power is greater than the next bonded validator by
// power, it'll be swapped.
func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validator) {
var newCliffVal types.Validator
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
oldCliffVal, found := k.GetValidator(ctx, cliffAddr)
if !found {
panic(fmt.Sprintf("cliff validator record not found for address: %X\n", cliffAddr))
}
// Create a validator iterator ranging from smallest to largest by power
// starting the current cliff validator's power.
start := GetValidatorsByPowerIndexKey(oldCliffVal, pool)
end := sdk.PrefixEndBytes(ValidatorsByPowerIndexKey)
iterator := store.Iterator(start, end)
if iterator.Valid() {
ownerAddr := iterator.Value()
currVal, found := k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
if currVal.Status != sdk.Bonded || currVal.Jailed {
panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr))
}
newCliffVal = currVal
iterator.Close()
} else {
panic("failed to create valid validator power iterator")
}
affectedValRank := GetValidatorsByPowerIndexKey(affectedVal, pool)
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
if bytes.Equal(affectedVal.OperatorAddr, newCliffVal.OperatorAddr) {
// The affected validator remains the cliff validator, however, since
// the store does not contain the new power, update the new power rank.
store.Set(ValidatorPowerCliffKey, affectedValRank)
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
// The affected validator no longer remains the cliff validator as it's
// power is greater than the new cliff validator.
k.setCliffValidator(ctx, newCliffVal, pool)
} else {
panic("invariant broken: the cliff validator should change or it should remain the same")
}
}
func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded {
newValidator = k.beginUnbondingValidator(ctx, newValidator)
// need to also clear the cliff validator spot because the jail has
// opened up a new spot which will be filled when
// updateValidatorsBonded is called
k.clearCliffValidator(ctx)
}
return newValidator
}
// nolint: unparam
func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool {
if oldFound && oldValidator.BondedTokens().LT(newValidator.BondedTokens()) {
return true
}
return false
}
// get the bond height and incremented intra-tx counter
// nolint: unparam
func (k Keeper) bondIncrement(
ctx sdk.Context, found bool, oldValidator types.Validator) (height int64, intraTxCounter int16) {
// if already a validator, copy the old block height and counter
if found && oldValidator.Status == sdk.Bonded {
height = oldValidator.BondHeight
intraTxCounter = oldValidator.BondIntraTxCounter
return
}
height = ctx.BlockHeight()
counter := k.GetIntraTxCounter(ctx)
intraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
return
}
func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidator,
newValidator types.Validator, pool types.Pool) (valPower []byte) {
store := ctx.KVStore(k.storeKey)
// update the list ordered by voting power
if oldFound {
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
}
valPower = GetValidatorsByPowerIndexKey(newValidator, pool)
store.Set(valPower, newValidator.OperatorAddr)
return valPower
}
// Update the bonded validator group based on a change to the validator
// affectedValidator. This function potentially adds the affectedValidator to
// the bonded validator group which kicks out the cliff validator. Under this
// situation this function returns the updated affectedValidator.
//
// The correct bonded subset of validators 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 all
// validators.
func (k Keeper) UpdateBondedValidators(
ctx sdk.Context, affectedValidator types.Validator) (
updatedVal types.Validator, updated bool) {
store := ctx.KVStore(k.storeKey)
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
maxValidators := k.GetParams(ctx).MaxValidators
bondedValidatorsCount := 0
var validator, validatorToBond types.Validator
newValidatorBonded := false
// create a validator iterator ranging from largest to smallest by power
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() {
// either retrieve the original validator from the store, or under the
// situation that this is the "affected validator" just use the
// validator provided because it has not yet been updated in the store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, affectedValidator.OperatorAddr) {
validator = affectedValidator
} else {
var found bool
validator, found = k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
}
// if we've reached jailed validators no further bonded validators exist
if validator.Jailed {
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("jailed validator cannot be bonded, address: %X\n", ownerAddr))
}
break
}
// increment the total number of bonded validators and potentially mark
// the validator to bond
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
bondedValidatorsCount++
}
iterator.Close()
if newValidatorBonded && bytes.Equal(oldCliffValidatorAddr, validator.OperatorAddr) {
panic("cliff validator has not been changed, yet we bonded a new validator")
}
// clear or set the cliff validator
if bondedValidatorsCount == int(maxValidators) {
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
} else if len(oldCliffValidatorAddr) > 0 {
k.clearCliffValidator(ctx)
}
// swap the cliff validator for a new validator if the affected validator
// was bonded
if newValidatorBonded {
if oldCliffValidatorAddr != nil {
oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
ensureValidatorFound(found, oldCliffValidatorAddr)
if bytes.Equal(validatorToBond.OperatorAddr, affectedValidator.OperatorAddr) {
// begin unbonding the old cliff validator iff the affected
// validator was newly bonded and has greater power
k.beginUnbondingValidator(ctx, oldCliffVal)
} else {
// otherwise begin unbonding the affected validator, which must
// have been kicked out
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
}
}
validator = k.bondValidator(ctx, validatorToBond)
if bytes.Equal(validator.OperatorAddr, affectedValidator.OperatorAddr) {
return validator, true
}
return affectedValidator, true
}
return types.Validator{}, false
}
// 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 := GetAddressFromValBondedIndexKey(iterator.Key())
toKickOut[string(ownerAddr)] = 0
}
iterator.Close()
var validator types.Validator
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
maxValidators := k.GetParams(ctx).MaxValidators
bondedValidatorsCount := 0
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() {
var found bool
ownerAddr := iterator.Value()
validator, found = k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
_, found = toKickOut[string(ownerAddr)]
if found {
delete(toKickOut, string(ownerAddr))
} else {
// If the validator wasn't in the toKickOut group it means it wasn't
// previously a validator, therefor update the validator to enter
// the validator group.
validator = k.bondValidator(ctx, validator)
}
if validator.Jailed {
// we should no longer consider jailed validators as they are ranked
// lower than any non-jailed/bonded validators
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr))
}
break
}
bondedValidatorsCount++
}
iterator.Close()
// clear or set the cliff validator
if bondedValidatorsCount == int(maxValidators) {
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
} else if len(oldCliffValidatorAddr) > 0 {
k.clearCliffValidator(ctx)
}
kickOutValidators(k, ctx, toKickOut)
return
}
func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
for key := range toKickOut {
ownerAddr := []byte(key)
validator, found := k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
k.beginUnbondingValidator(ctx, validator)
}
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
params := k.GetParams(ctx)
// sanity check
if validator.Status == sdk.Unbonded ||
validator.Status == sdk.Unbonding {
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
k.SetPool(ctx, pool)
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
validator.UnbondingHeight = ctx.BlockHeader().Height
// save the now unbonded validator record
k.SetValidator(ctx, validator)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
// also remove from the Bonded types.Validators Store
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
k.OnValidatorBeginUnbonding(ctx, validator.ConsAddress())
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))
}
validator.BondHeight = ctx.BlockHeight()
// 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
k.SetValidator(ctx, validator)
store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{})
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
k.OnValidatorBonded(ctx, validator.ConsAddress())
return validator
}
// remove the validator record and associated indexes
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
k.OnValidatorRemoved(ctx, 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(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address())))
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.OperatorAddr)) == nil {
return
}
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(address), bz)
}
// UpdateValidatorCommission attempts to update a validator's commission rate.
// An error is returned if the new commission rate is invalid.
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) sdk.Error {
commission := validator.Commission
blockTime := ctx.BlockHeader().Time
if err := commission.ValidateNewRate(newRate, blockTime); err != nil {
return err
}
validator.Commission.Rate = newRate
validator.Commission.UpdateTime = blockTime
k.SetValidator(ctx, validator)
k.OnValidatorCommissionChange(ctx, validator.OperatorAddr)
return nil
}
//__________________________________________________________________________
// 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.OperatorAddr)
}
// 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)
}
func ensureValidatorFound(found bool, ownerAddr []byte) {
if !found {
panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr))
}
}

View File

@ -13,18 +13,6 @@ import (
"github.com/stretchr/testify/require"
)
// for testing, remove all validator update entries after applied to Tendermint
func clearTendermintUpdates(ctx sdk.Context, k Keeper) {
store := ctx.TransientStore(k.storeTKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesTKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//_______________________________________________________
func TestSetValidator(t *testing.T) {
@ -41,11 +29,17 @@ func TestSetValidator(t *testing.T) {
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
// after the save the validator should be bonded
// ensure update
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, valAddr)
require.True(t, found)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidatorUpdate(), updates[0])
// after the save the validator should be bonded
require.Equal(t, sdk.Bonded, validator.Status)
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
@ -59,7 +53,7 @@ func TestSetValidator(t *testing.T) {
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
resVals = keeper.GetValidatorsByPower(ctx)
resVals = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, 1, len(resVals))
require.True(ValEq(t, validator, resVals[0]))
@ -71,10 +65,6 @@ func TestSetValidator(t *testing.T) {
require.Equal(t, 1, len(resVals))
require.True(ValEq(t, validator, resVals[0]))
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidator(), updates[0])
allVals := keeper.GetAllValidators(ctx)
require.Equal(t, 1, len(allVals))
}
@ -94,27 +84,28 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
testingUpdateValidator(keeper, ctx, validator)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
pool = keeper.GetPool(ctx)
power := GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
require.True(t, validatorByPowerIndexExists(keeper, ctx, power))
// burn half the delegator shares
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2)))
require.Equal(t, int64(50), burned.RoundInt64())
keeper.SetPool(ctx, pool) // update the pool
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
require.False(t, keeper.validatorByPowerIndexExists(ctx, power))
keeper.SetPool(ctx, pool) // update the pool
testingUpdateValidator(keeper, ctx, validator) // update the validator, possibly kicking it out
require.False(t, validatorByPowerIndexExists(keeper, ctx, power))
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
power = GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
require.True(t, validatorByPowerIndexExists(keeper, ctx, power))
}
func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
@ -144,7 +135,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10)))
keeper.SetPool(ctx, pool)
val = keeper.UpdateValidator(ctx, val)
val = testingUpdateValidator(keeper, ctx, val)
validators[i] = val
}
@ -152,17 +143,10 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
// remove enough tokens to kick out the validator below the current cliff
// validator and next in line cliff validator
keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal, pool)
nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21))
keeper.SetPool(ctx, pool)
nextCliffVal = keeper.UpdateValidator(ctx, nextCliffVal)
// require the cliff validator has changed
cliffVal := validators[numVals-maxVals-1]
require.Equal(t, cliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// require the cliff validator power has changed
cliffPower := keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(cliffVal, pool), cliffPower)
nextCliffVal = testingUpdateValidator(keeper, ctx, nextCliffVal)
expectedValStatus := map[int]sdk.BondStatus{
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
@ -182,75 +166,6 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
}
}
func TestCliffValidatorChange(t *testing.T) {
numVals := 10
maxVals := 5
// create context, keeper, and pool for tests
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
// create keeper parameters
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(maxVals)
keeper.SetParams(ctx, params)
// create a random pool
pool.LooseTokens = sdk.NewDec(10000)
pool.BondedTokens = sdk.NewDec(1234)
keeper.SetPool(ctx, pool)
validators := make([]types.Validator, numVals)
for i := 0; i < len(validators); i++ {
moniker := fmt.Sprintf("val#%d", int64(i))
val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker})
val.BondHeight = int64(i)
val.BondIntraTxCounter = int16(i)
val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10)))
keeper.SetPool(ctx, pool)
val = keeper.UpdateValidator(ctx, val)
validators[i] = val
}
// add a large amount of tokens to current cliff validator
currCliffVal := validators[numVals-maxVals]
currCliffVal, pool, _ = currCliffVal.AddTokensFromDel(pool, sdk.NewInt(200))
keeper.SetPool(ctx, pool)
currCliffVal = keeper.UpdateValidator(ctx, currCliffVal)
// assert new cliff validator to be set to the second lowest bonded validator by power
newCliffVal := validators[numVals-maxVals+1]
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// assert cliff validator power should have been updated
cliffPower := keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
// add small amount of tokens to new current cliff validator
newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
newCliffVal = keeper.UpdateValidator(ctx, newCliffVal)
// assert cliff validator has not change but increased in power
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
// add enough power to cliff validator to be equal in rank to next validator
newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(9))
keeper.SetPool(ctx, pool)
newCliffVal = keeper.UpdateValidator(ctx, newCliffVal)
// assert new cliff validator due to power rank construction
newCliffVal = validators[numVals-maxVals+2]
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// assert cliff validator power should have been updated
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
}
func TestSlashToZeroPowerRemoved(t *testing.T) {
// initialize setup
ctx, _, keeper := CreateTestInput(t, false, 100)
@ -263,12 +178,14 @@ func TestSlashToZeroPowerRemoved(t *testing.T) {
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
keeper.SetValidatorByConsAddr(ctx, validator)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
// slash the validator by 100%
consAddr0 := sdk.ConsAddress(PKs[0].Address())
keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// validator should have been deleted
_, found := keeper.GetValidator(ctx, addrVals[0])
require.False(t, found)
@ -306,7 +223,7 @@ func TestValidatorBasics(t *testing.T) {
assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens))
// set and retrieve a record
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
keeper.SetValidatorByConsAddr(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
@ -333,7 +250,7 @@ func TestValidatorBasics(t *testing.T) {
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(10)
validators[0].DelegatorShares = sdk.NewDec(10)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resVal, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
@ -343,8 +260,8 @@ func TestValidatorBasics(t *testing.T) {
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])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
resVal, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
assert.True(ValEq(t, validators[1], resVal))
@ -364,7 +281,7 @@ func TestValidatorBasics(t *testing.T) {
require.False(t, found)
}
// test how the validators are sorted, tests GetValidatorsByPower
// test how the validators are sorted, tests GetBondedValidatorsByPower
func GetValidatorSortingUnmixed(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
@ -377,11 +294,11 @@ func GetValidatorSortingUnmixed(t *testing.T) {
validators[i].Status = sdk.Bonded
validators[i].Tokens = sdk.NewDec(amt)
validators[i].DelegatorShares = sdk.NewDec(amt)
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators)
assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators)
@ -396,15 +313,15 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test a basic increase in voting power
validators[3].Tokens = sdk.NewDec(500)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
// test a decrease in voting power
validators[3].Tokens = sdk.NewDec(300)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -412,8 +329,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test equal voting power, different age
validators[3].Tokens = sdk.NewDec(200)
ctx = ctx.WithBlockHeight(10)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -422,8 +339,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// no change in voting power - no change in sort
ctx = ctx.WithBlockHeight(20)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -431,12 +348,12 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// change in voting power of both validators, both still in v-set, no age change
validators[3].Tokens = sdk.NewDec(300)
validators[4].Tokens = sdk.NewDec(300)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
ctx = ctx.WithBlockHeight(30)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.GetBondedValidatorsByPower(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]))
@ -473,7 +390,7 @@ func GetValidatorSortingMixed(t *testing.T) {
validators[4].Tokens = sdk.NewDec(amts[4])
for i := range amts {
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
val0, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[0]))
require.True(t, found)
@ -492,7 +409,7 @@ func GetValidatorSortingMixed(t *testing.T) {
require.Equal(t, sdk.Bonded, val4.Status)
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators)
assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators)
@ -525,24 +442,26 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
moniker := fmt.Sprintf("val#%d", int64(i))
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
validators[i] = testingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr)
require.True(t, found)
}
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(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)
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
@ -556,28 +475,31 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
validators[3], found = keeper.GetValidator(ctx, validators[3].OperatorAddr)
require.True(t, found)
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(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
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(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
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(200))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
@ -600,34 +522,39 @@ func TestValidatorBondHeight(t *testing.T) {
validators[0] = types.NewValidator(sdk.ValAddress(Addrs[0]), PKs[0], types.Description{})
validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{})
validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{})
validators[0].BondIntraTxCounter = 0
validators[1].BondIntraTxCounter = 1
validators[2].BondIntraTxCounter = 2
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(200))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(100))
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(100))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, 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])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
pool = keeper.GetPool(ctx)
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(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]))
keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool)
keeper.DeleteValidatorByPowerIndex(ctx, validators[2], pool)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(50))
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(50))
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
@ -646,20 +573,21 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
var found bool
validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr)
require.True(t, found)
}
assert.Equal(t, sdk.Unbonding, validators[0].Status)
assert.Equal(t, sdk.Unbonded, validators[0].Status)
assert.Equal(t, sdk.Unbonding, 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)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
assert.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]))
@ -668,14 +596,14 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(600))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
func TestGetValidTendermintUpdatesAllNone(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -693,17 +621,22 @@ func TestGetValidTendermintUpdatesAllNone(t *testing.T) {
// test from nothing to something
// tendermintUpdate set: {} -> {c1, c3}
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
keeper.SetValidator(ctx, validators[1])
keeper.SetValidatorByPowerIndex(ctx, validators[1], pool)
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
assert.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].ABCIValidator(), updates[0])
assert.Equal(t, validators[1].ABCIValidator(), updates[1])
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
}
func TestGetValidTendermintUpdatesIdentical(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -714,19 +647,18 @@ func TestGetValidTendermintUpdatesIdentical(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
}
func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -737,24 +669,23 @@ func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(600)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
}
func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -765,10 +696,9 @@ func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
@ -776,16 +706,16 @@ func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) {
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(190))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(80))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
}
func TestGetValidTendermintUpdatesInserted(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20, 5, 15, 25}
@ -796,36 +726,42 @@ func TestGetValidTendermintUpdatesInserted(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetValidTendermintUpdates(ctx)
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[2])
keeper.SetValidatorByPowerIndex(ctx, validators[2], pool)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[2].ABCIValidator(), updates[0])
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
clearTendermintUpdates(ctx, keeper)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
updates = keeper.GetValidTendermintUpdates(ctx)
pool = keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[3])
keeper.SetValidatorByPowerIndex(ctx, validators[3], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[3], _ = keeper.GetValidator(ctx, validators[3].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[3].ABCIValidator(), updates[0])
require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
clearTendermintUpdates(ctx, keeper)
validators[4] = keeper.UpdateValidator(ctx, validators[4])
updates = keeper.GetValidTendermintUpdates(ctx)
pool = keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[4])
keeper.SetValidatorByPowerIndex(ctx, validators[4], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[4], _ = keeper.GetValidator(ctx, validators[4].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[4].ABCIValidator(), updates[0])
require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0])
}
func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := types.DefaultParams()
params.MaxValidators = 2
@ -839,34 +775,33 @@ func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetValidTendermintUpdates(ctx)
testingUpdateValidator(keeper, ctx, validators[2])
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 0, len(updates))
// test validator change its power and become a gotValidator (pushing out an existing)
// tendermintUpdate set: {} -> {c0, c4}
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
pool := keeper.GetPool(ctx)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(10))
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates = keeper.GetValidTendermintUpdates(ctx)
keeper.SetValidator(ctx, validators[2])
keeper.SetValidatorByPowerIndex(ctx, validators[2], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
require.Equal(t, 2, len(updates), "%v", updates)
require.Equal(t, validators[0].ABCIValidatorZero(), updates[0])
require.Equal(t, validators[2].ABCIValidator(), updates[1])
require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1])
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
}
func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 100}
@ -875,12 +810,12 @@ func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// check initial power
require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64())
@ -892,21 +827,21 @@ func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) {
validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20))
validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
// power has changed
require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64())
require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64())
// Tendermint updates should reflect power change
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
}
func TestGetValidTendermintUpdatesNewValidator(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(3)
@ -923,28 +858,33 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) {
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{})
validators[i].BondIntraTxCounter = int16(i)
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// verify initial Tendermint updates are correct
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, len(validators), len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// update initial validator set
for i, amt := range amts {
pool := keeper.GetPool(ctx)
keeper.DeleteValidatorByPowerIndex(ctx, validators[i], pool)
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// add a new validator that goes from zero power, to non-zero power, back to
@ -958,10 +898,11 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) {
validator, pool, _ = validator.AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
validator, pool, _ = validator.RemoveDelShares(pool, sdk.NewDecFromInt(amt))
validator = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
// add a new validator that increases in power
valPubKey = PKs[len(validators)+2]
@ -969,19 +910,22 @@ func TestGetValidTendermintUpdatesNewValidator(t *testing.T) {
validator = types.NewValidator(valAddr, valPubKey, types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
// verify initial Tendermint updates are correct
updates = keeper.GetValidTendermintUpdates(ctx)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, _ = keeper.GetValidator(ctx, validator.OperatorAddr)
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, len(validators)+1, len(updates))
require.Equal(t, validator.ABCIValidator(), updates[0])
require.Equal(t, validators[0].ABCIValidator(), updates[1])
require.Equal(t, validators[1].ABCIValidator(), updates[2])
require.Equal(t, validator.ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2])
}
func TestGetValidTendermintUpdatesBondTransition(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(2)
@ -1000,60 +944,67 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) {
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// verify initial Tendermint updates are correct
updates := keeper.GetValidTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[2].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// delegate to validator with lowest power but not enough to bond
ctx = ctx.WithBlockHeight(1)
pool := keeper.GetPool(ctx)
validator, found := keeper.GetValidator(ctx, validators[0].OperatorAddr)
var found bool
validators[0], found = keeper.GetValidator(ctx, validators[0].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(1))
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
// verify initial Tendermint updates are correct
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// create a series of events that will bond and unbond the validator with
// lowest power in a single block context (height)
ctx = ctx.WithBlockHeight(2)
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, validators[1].OperatorAddr)
validators[1], found = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.RemoveDelShares(pool, validator.DelegatorShares)
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].RemoveDelShares(pool, validators[0].DelegatorShares)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(250))
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 0, len(updates))
keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(250))
keeper.SetPool(ctx, pool)
validators[1] = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validators[1])
keeper.SetValidatorByPowerIndex(ctx, validators[1], pool)
// verify initial Tendermint updates are correct
updates = keeper.GetValidTendermintUpdates(ctx)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[1].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
}
func TestUpdateValidatorCommission(t *testing.T) {
@ -1072,6 +1023,9 @@ func TestUpdateValidatorCommission(t *testing.T) {
val1, _ = val1.SetInitialCommission(commission1)
val2, _ = val2.SetInitialCommission(commission2)
keeper.SetValidator(ctx, val1)
keeper.SetValidator(ctx, val2)
testCases := []struct {
validator types.Validator
newRate sdk.Dec
@ -1085,11 +1039,13 @@ func TestUpdateValidatorCommission(t *testing.T) {
}
for i, tc := range testCases {
err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate)
commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate)
if tc.expectedErr {
require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate)
} else {
tc.validator.Commission = commission
keeper.SetValidator(ctx, tc.validator)
val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddr)
require.True(t, found,

View File

@ -71,8 +71,8 @@ func TestQueryValidators(t *testing.T) {
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.SetValidator(ctx, validators[0])
keeper.SetValidator(ctx, validators[1])
// Query Validators
queriedValidators := keeper.GetValidators(ctx, params.MaxValidators)
@ -114,9 +114,14 @@ func TestQueryDelegation(t *testing.T) {
// Create Validators and Delegation
val1 := types.NewValidator(addrVal1, pk1, types.Description{})
keeper.SetValidator(ctx, val1)
pool := keeper.GetPool(ctx)
keeper.SetValidatorByPowerIndex(ctx, val1, pool)
keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true)
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// Query Delegator bonded validators
queryParams := newTestDelegatorQuery(addrAcc2)
bz, errRes := cdc.MarshalJSON(queryParams)

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