Merge branch 'develop' into greg/testnet-command-2

This commit is contained in:
Greg Szabo 2018-06-14 11:34:26 -07:00 committed by GitHub
commit f8290a0fc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3022 additions and 958 deletions

View File

@ -4,6 +4,9 @@
*TBD*
BREAKING CHANGES
* Change default ports from 466xx to 266xx
## 0.19.0
*June 13, 2018*
@ -14,6 +17,7 @@ BREAKING CHANGES
FEATURES
* [x/auth] Added AccountNumbers to BaseAccount and StdTxs to allow for replay protection with account pruning
* [lcd] added an endpoint to query for the SDK version of the connected node
IMPROVEMENTS
* export command now writes current validator set for Tendermint
@ -26,14 +30,18 @@ IMPROVEMENTS
FIXES
* Fixes consensus fault on testnet - see postmortem [here](https://github.com/cosmos/cosmos-sdk/issues/1197#issuecomment-396823021)
* [x/stake] bonded inflation removed, non-bonded inflation partially implemented
* [x/stake] bonded inflation removed, non-bonded inflation partially implemented
* [lcd] Switch to bech32 for addresses on all human readable inputs and outputs
* [lcd] fixed tx indexing/querying
* [cli] Added `--gas` flag to specify transaction gas limit
* [gaia] Registered slashing message handler
* [x/slashing] Set signInfo.StartHeight correctly for newly bonded validators
## 0.18.0
FEATURES
* [docs] Reorganize documentation
* [docs] Update staking spec, create WIP spec for slashing, and fees
## 0.18.0
*June 9, 2018*

9
Gopkg.lock generated
View File

@ -267,8 +267,8 @@
"server",
"types"
]
revision = "ebee2fe114020aa49c70bbbae50b7079fc7e7b90"
version = "v0.11.0"
revision = "198dccf0ddfd1bb176f87657e3286a05a6ed9540"
version = "v0.12.0"
[[projects]]
branch = "master"
@ -347,8 +347,7 @@
"types",
"version"
]
revision = "27bd1deabe4ba6a2d9b463b8f3e3f1e31b993e61"
version = "v0.20.0"
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
[[projects]]
branch = "develop"
@ -463,6 +462,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "31f69b235b2d8f879a215c9e8ca0919adc62d21f6830b17931a3a0efb058721f"
inputs-digest = "d02a24bcfd8bded901e1b154e19b81ff797d3921046ede19d1d11eed61e871e7"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -54,7 +54,7 @@
[[constraint]]
name = "github.com/tendermint/abci"
version = "=0.11.0"
version = "=0.12.0"
[[constraint]]
name = "github.com/tendermint/go-crypto"
@ -70,7 +70,7 @@
[[constraint]]
name = "github.com/tendermint/tendermint"
version = "=0.20.0"
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
[[override]]
name = "github.com/tendermint/tmlibs"

View File

@ -14,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
@ -338,6 +339,11 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
} else {
result = app.Simulate(tx)
}
case "version":
return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK),
Value: []byte(version.GetVersion()),
}
default:
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
}

View File

@ -10,7 +10,7 @@ import (
func RunForever(app abci.Application) {
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", app)
srv, err := server.NewServer("0.0.0.0:26658", "socket", app)
if err != nil {
cmn.Exit(err.Error())
}

View File

@ -3,6 +3,8 @@ package context
import (
"fmt"
"github.com/tendermint/tmlibs/common"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/wire"
@ -42,14 +44,19 @@ func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit,
return res, err
}
// Query from Tendermint with the provided key and storename
func (ctx CoreContext) Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
return ctx.query(key, storeName, "key")
// Query information about the connected node
func (ctx CoreContext) Query(path string) (res []byte, err error) {
return ctx.query(path, nil)
}
// QueryStore from Tendermint with the provided key and storename
func (ctx CoreContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) {
return ctx.queryStore(key, storeName, "key")
}
// Query from Tendermint with the provided storename and subspace
func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName string) (res []sdk.KVPair, err error) {
resRaw, err := ctx.query(subspace, storeName, "subspace")
resRaw, err := ctx.queryStore(subspace, storeName, "subspace")
if err != nil {
return res, err
}
@ -58,8 +65,7 @@ func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName
}
// Query from Tendermint with the provided storename and path
func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
func (ctx CoreContext) query(path string, key common.HexBytes) (res []byte, err error) {
node, err := ctx.GetNode()
if err != nil {
return res, err
@ -80,6 +86,12 @@ func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res [
return resp.Value, nil
}
// Query from Tendermint with the provided storename and path
func (ctx CoreContext) queryStore(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}
// Get the from address from the name flag
func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) {
@ -177,7 +189,7 @@ func (ctx CoreContext) GetAccountNumber(address []byte) (int64, error) {
return 0, errors.New("accountDecoder required but not provided")
}
res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore)
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
if err != nil {
return 0, err
}
@ -201,7 +213,7 @@ func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
return 0, errors.New("accountDecoder required but not provided")
}
res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore)
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
if err != nil {
return 0, err
}

View File

@ -25,7 +25,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
// TODO: make this default false when we support proofs
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block")
}
return cmds
@ -39,7 +39,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction")
}
return cmds

View File

@ -116,6 +116,15 @@ func TestVersion(t *testing.T) {
require.Nil(t, err)
match := reg.MatchString(body)
assert.True(t, match, body)
// node info
res, body = Request(t, port, "GET", "/node_version", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
require.Nil(t, err)
match = reg.MatchString(body)
assert.True(t, match, body)
}
func TestNodeStatus(t *testing.T) {

View File

@ -17,7 +17,6 @@ import (
keys "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tx "github.com/cosmos/cosmos-sdk/client/tx"
version "github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
@ -57,13 +56,12 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
cmd.Flags().StringP(flagListenAddr, "a", "tcp://localhost:1317", "Address for server to listen on")
cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)")
cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
return cmd
}
func createHandler(cdc *wire.Codec) http.Handler {
r := mux.NewRouter()
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
kb, err := keys.GetKeyBase() //XXX
if err != nil {
@ -73,6 +71,8 @@ func createHandler(cdc *wire.Codec) http.Handler {
ctx := context.NewCoreContextFromViper()
// TODO make more functional? aka r = keys.RegisterRoutes(r)
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
r.HandleFunc("/node_version", NodeVersionRequestHandler(cdc, ctx)).Methods("GET")
keys.RegisterRoutes(r)
rpc.RegisterRoutes(ctx, r)
tx.RegisterRoutes(ctx, r, cdc)

29
client/lcd/version.go Normal file
View File

@ -0,0 +1,29 @@
package lcd
import (
"fmt"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
)
// cli version REST handler endpoint
func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) {
v := version.GetVersion()
w.Write([]byte(v))
}
// connected node version REST handler endpoint
func NodeVersionRequestHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
version, err := ctx.Query("/app/version")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error())))
return
}
w.Write([]byte(version))
}
}

View File

@ -24,7 +24,7 @@ func BlockCommand() *cobra.Command {
Args: cobra.MaximumNArgs(1),
RunE: printBlock,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)")

View File

@ -36,7 +36,7 @@ func initClientCommand() *cobra.Command {
RunE: todoNotImplemented,
}
cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity")
cmd.Flags().String(flagCommit, "", "File with trusted and signed header")
cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)")

View File

@ -18,7 +18,7 @@ func statusCommand() *cobra.Command {
Short: "Query remote node for status",
RunE: printNodeStatus,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
return cmd
}

View File

@ -24,7 +24,7 @@ func ValidatorCommand() *cobra.Command {
Args: cobra.MaximumNArgs(1),
RunE: printValidators,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
return cmd

View File

@ -42,7 +42,7 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
},
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")

View File

@ -43,7 +43,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
},
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false once proofs built in
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")

View File

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -50,7 +49,7 @@ func TestGaiaCLISend(t *testing.T) {
assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak"))
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
time.Sleep(time.Second * 2) // waiting for some blocks to pass
tests.WaitForNextHeightTM(port)
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
@ -59,7 +58,7 @@ func TestGaiaCLISend(t *testing.T) {
// test autosequencing
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
time.Sleep(time.Second * 2) // waiting for some blocks to pass
tests.WaitForNextHeightTM(port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak"))
@ -96,7 +95,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.NoError(t, err)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
tests.WaitForNextHeightTM(port)
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
@ -112,7 +111,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
executeWrite(t, cvStr, pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
tests.WaitForNextHeightTM(port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
@ -131,7 +130,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr))
executeWrite(t, unbondStr, pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
tests.WaitForNextHeightTM(port)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
@ -150,6 +149,8 @@ func executeWrite(t *testing.T, cmdStr string, writes ...string) {
require.NoError(t, err)
}
proc.Wait()
// bz := proc.StdoutBuffer.Bytes()
// fmt.Println("EXEC WRITE", string(bz))
}
func executeInit(t *testing.T, cmdStr string) (chainID string) {

View File

@ -0,0 +1,35 @@
# Gaiadebug
Simple tool for simple debugging.
We try to accept both hex and base64 formats and provide a useful response.
Note we often encode bytes as hex in the logs, but as base64 in the JSON.
## Pubkeys
The following give the same result:
```
gaiadebug pubkey TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4=
gaiadebug pubkey 4D94D09DFA8EB22F3D49EA17567230FAD9C5267AF85FCA950B453C02C126164E
```
## Txs
Pass in a hex/base64 tx and get back the full JSON
```
gaiadebug tx <hex or base64 transaction>
```
## Hack
This is a command with boilerplate for using Go as a scripting language to hack
on an existing Gaia state.
Currently we have an example for the state of gaia-6001 after it
[crashed](https://github.com/cosmos/cosmos-sdk/blob/master/cmd/gaia/testnets/STATUS.md#june-13-2018-230-est---published-postmortem-of-gaia-6001-failure).
If you run `gaiadebug hack $HOME/.gaiad` on that
state, it will do a binary search on the state history to find when the state
invariant was violated.

View File

@ -1,4 +1,4 @@
# Connect to the `gaia-6001` Testnet
# Connect to the `gaia-6002` Testnet
Note: We are aware this documentation is sub-par. We are working to
improve the tooling and the documentation to make this process as painless as
@ -23,7 +23,7 @@ Next, let's install the testnet's version of the Cosmos SDK.
mkdir -p $GOPATH/src/github.com/cosmos
cd $GOPATH/src/github.com/cosmos
git clone https://github.com/cosmos/cosmos-sdk
cd cosmos-sdk && git checkout v0.18.0
cd cosmos-sdk && git checkout v0.19.0
make get_tools && make get_vendor_deps && make install
```
@ -31,7 +31,7 @@ That will install the `gaiad` and `gaiacli` binaries. Verify that everything is
```
gaiad version
0.18.0-eceb56b7
0.19.0-<commit>
```
### Node Setup
@ -76,7 +76,7 @@ Now it is time to upgrade the software:
```
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
git fetch --all && git checkout v0.18.0
git fetch --all && git checkout v0.19.0
make update_tools && make get_vendor_deps && make install
```
@ -90,7 +90,7 @@ Copy the testnet's `genesis.json` file and place it in `gaiad`'s config director
```
mkdir -p $HOME/.gaiad/config
cp -a $GOPATH/src/github.com/cosmos/cosmos-sdk/cmd/gaia/testnets/gaia-6001/genesis.json $HOME/.gaiad/config/genesis.json
cp -a $GOPATH/src/github.com/cosmos/cosmos-sdk/cmd/gaia/testnets/gaia-6002/genesis.json $HOME/.gaiad/config/genesis.json
```
### Add Seed Nodes
@ -99,7 +99,7 @@ Your node needs to know how to find peers. You'll need to add healthy seed nodes
```
# Comma separated list of seed nodes to connect to
seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6001.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656"
seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6002.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656"
```
You can also [ask other validators](https://riot.im/app/#/room/#cosmos_validators:matrix.org) for a persistent peer and add it under the `persistent_peers` key. For more information on seeds and peers, [read this](https://github.com/tendermint/tendermint/blob/develop/docs/using-tendermint.md#peers).

View File

@ -1,5 +1,26 @@
# TESTNET STATUS
## *June 13, 2018, 17:00 EST* - Gaia-6002 is making blocks!
- Gaia-6002 is live and making blocks
- Absent validators have been slashed and revoked
- Currently live with 17 validators
## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002
- After fixing bugs from gaia-6001, especially [issue
#1197](https://github.com/cosmos/cosmos-sdk/issues/1197), we are announcing a
new testnet, Gaia-6002
- Gaia-6002 has the same genesis file as Gaia-6001, just with the chain-id
updated
- Update from previous testnet [here](https://github.com/cosmos/cosmos-sdk/tree/master/cmd/gaia/testnets#upgrading-from-previous-testnet)
## *June 13, 2018, 4:30 EST* - New Release
- Released gaia
[v0.19.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.19.0)
- Includes various bug-fixes for staking found on Gaia-6001
## *June 13, 2018, 2:30 EST* - Published Postmortem of Gaia-6001 failure
- A bug in the design of the staking data model caused a sanity check to fail

File diff suppressed because it is too large Load Diff

View File

@ -78,7 +78,7 @@ window. Here run:
::
basecli init --node=tcp://localhost:46657 --genesis=$HOME/.basecoin/genesis.json
basecli init --node=tcp://localhost:26657 --genesis=$HOME/.basecoin/genesis.json
If you provide the genesis file to basecli, it can calculate the proper
chainID and validator hash. Basecli needs to get this information from

View File

@ -49,7 +49,7 @@ initialize the light-client and send a transaction:
::
countercli init --node=tcp://localhost:46657 --genesis=$HOME/.counter/genesis.json
countercli init --node=tcp://localhost:26657 --genesis=$HOME/.counter/genesis.json
YOU=$(countercli keys get friend | awk '{print $2}')
countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1

View File

@ -39,18 +39,18 @@ and ports. It should look like:
::
proxy_app = "tcp://127.0.0.1:46668"
proxy_app = "tcp://127.0.0.1:26668"
moniker = "anonymous"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"
[rpc]
laddr = "tcp://0.0.0.0:46667"
laddr = "tcp://0.0.0.0:26667"
[p2p]
laddr = "tcp://0.0.0.0:46666"
seeds = "0.0.0.0:46656"
laddr = "tcp://0.0.0.0:26666"
seeds = "0.0.0.0:26656"
Start Nodes
-----------
@ -69,14 +69,14 @@ account:
::
gaia client init --chain-id=gaia-test --node=tcp://localhost:46657
gaia client init --chain-id=gaia-test --node=tcp://localhost:26657
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
To see what tendermint considers the validator set is, use:
::
curl localhost:46657/validators
curl localhost:26657/validators
and compare the information in this file: ``~/.gaia1/priv_validator.json``. The ``address`` and ``pub_key`` fields should match.

View File

@ -49,7 +49,7 @@ Finally, let's initialize the gaia client to interact with the testnet:
::
gaia client init --chain-id=gaia-1 --node=tcp://localhost:46657
gaia client init --chain-id=gaia-1 --node=tcp://localhost:26657
and check our balance:

View File

@ -38,9 +38,15 @@ templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
from recommonmark.parser import CommonMarkParser
source_parsers = {
'.md': CommonMarkParser,
}
source_suffix = ['.rst', '.md']
#source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
@ -69,7 +75,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'old']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_attic', 'spec']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

View File

@ -0,0 +1,59 @@
# Install
The fastest and easiest way to install the Cosmos SDK binaries
is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_ubuntu.sh) on a fresh Ubuntu instance. Similarly, you can run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_bsd.sh) on a fresh FreeBSD instance. Read the scripts before running them to ensure no untrusted connection is being made, for example we're making curl requests to download golang. Also read the comments / instructions carefully (i.e., reset your terminal after running the script).
Cosmos SDK can be installed to
`$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program:
```
go get github.com/cosmos/cosmos-sdk
```
If the dependencies have been updated with breaking changes, or if
another branch is required, `dep` is used for dependency management.
Thus, assuming you've already run `go get` or otherwise cloned the repo,
the correct way to install is:
```
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
make get_tools
make get_vendor_deps
make install
make install_examples
```
This will install `gaiad` and `gaiacli` and four example binaries:
`basecoind`, `basecli`, `democoind`, and `democli`.
Verify that everything is OK by running:
```
gaiad version
```
you should see:
```
0.17.3-a5a78eb
```
then with:
```
gaiacli version
```
you should see the same version (or a later one for both).
## Update
Get latest code (you can also `git fetch` only the version desired),
ensure the dependencies are up to date, then recompile.
```
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
git fetch -a origin
git checkout VERSION
make get_vendor_deps
make install
```

View File

@ -0,0 +1,7 @@
# Key Management
Here we cover many aspects of handling keys within the Cosmos SDK
framework.
// TODO add relevant key discussion
(related https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography)

View File

@ -17,6 +17,13 @@ paths:
responses:
200:
description: Plaintext version i.e. "v0.5.0"
/node_version:
get:
summary: Version of the connected node
description: Get the version of the SDK running on the connected node to compare against expected
responses:
200:
description: Plaintext version i.e. "v0.5.0"
/node_info:
get:
description: Only the node info. Block information can be queried via /block/latest
@ -41,7 +48,7 @@ paths:
type: string
listen_addr:
type: string
example: 192.168.56.1:46656
example: 192.168.56.1:26656
version:
description: Tendermint version
type: string

View File

@ -291,7 +291,7 @@ To confirm for certain the new validator is active, ask the tendermint node:
::
curl localhost:46657/validators
curl localhost:26657/validators
If you now kill either node, blocks will stop streaming in, because
there aren't enough validators online. Turn it back on and they will

View File

@ -0,0 +1,216 @@
//TODO update .rst
# Staking Module
## Overview
The Cosmos Hub is a Tendermint-based Delegated Proof of Stake (DPos) blockchain
system that serves as a backbone of the Cosmos ecosystem. It is operated and
secured by an open and globally decentralized set of validators. Tendermint is
a Byzantine fault-tolerant distributed protocol for consensus among distrusting
parties, in this case the group of validators which produce the blocks for the
Cosmos Hub. To avoid the nothing-at-stake problem, a validator in Tendermint
needs to lock up coins in a bond deposit. Each bond's atoms are illiquid, they
cannot be transferred - in order to become liquid, they must be unbonded, a
process which will take 3 weeks by default at Cosmos Hub launch. Tendermint
protocol messages are signed by the validator's private key and are therefor
attributable. Validators acting outside protocol specifications can be made
accountable through punishing by slashing (burning) their bonded Atoms. On the
other hand, validators are rewarded for their service of securing blockchain
network by the inflationary provisions and transactions fees. This incentivizes
correct behavior of the validators and provides the economic security of the
network.
The native token of the Cosmos Hub is called the Atom; becoming a validator of the
Cosmos Hub requires holding Atoms. However, not all Atom holders are validators
of the Cosmos Hub. More precisely, there is a selection process that determines
the validator set as a subset of all validators (Atom holders that
want to become a validator). The other option for Atom holders is to delegate
their atoms to validators, i.e., being a delegator. A delegator is an Atom
holder that has put its Atoms at stake by delegating it to a validator. By bonding
Atoms to secure the network (and taking a risk of being slashed in case of
misbehaviour), a user is rewarded with inflationary provisions and transaction
fees proportional to the amount of its bonded Atoms. The Cosmos Hub is
designed to efficiently facilitate a small numbers of validators (hundreds),
and large numbers of delegators (tens of thousands). More precisely, it is the
role of the Staking module of the Cosmos Hub to support various staking
functionality including validator set selection, delegating, bonding and
withdrawing Atoms, and the distribution of inflationary provisions and
transaction fees.
## Basic Terms and Definitions
* Cosmsos Hub - a Tendermint-based Delegated Proof of Stake (DPos)
blockchain system
* Atom - native token of the Cosmsos Hub
* Atom holder - an entity that holds some amount of Atoms
* Pool - Global object within the Cosmos Hub which accounts global state
including the total amount of bonded, unbonding, and unbonded atoms
* Validator Share - Share which a validator holds to represent its portion of
bonded, unbonding or unbonded atoms in the pool
* Delegation Share - Shares which a delegation bond holds to represent its
portion of bonded, unbonding or unbonded shares in a validator
* Bond Atoms - a process of locking Atoms in a delegation share which holds them
under protocol control.
* Slash Atoms - the process of burning atoms in the pool and assoiated
validator shares of a misbehaving validator, (not behaving according to the
protocol specification). This process devalues the worth of delegation shares
of the given validator
* Unbond Shares - Process of retrieving atoms from shares. If the shares are
bonded the shares must first remain in an inbetween unbonding state for the
duration of the unbonding period
* Redelegating Shares - Process of redelegating atoms from one validator to
another. This process is instantaneous, but the redelegated atoms are
retrospecively slashable if the old validator is found to misbehave for any
blocks before the redelegation. These atoms are simultaniously slashable
for any new blocks which the new validator misbehavess
* Validator - entity with atoms which is either actively validating the Tendermint
protocol (bonded validator) or vying to validate .
* Bonded Validator - a validator whose atoms are currently bonded and liable to
be slashed. These validators are to be able to sign protocol messages for
Tendermint consensus. At Cosmos Hub genesis there is a maximum of 100
bonded validator positions. Only Bonded Validators receive atom provisions
and fee rewards.
* Delegator - an Atom holder that has bonded Atoms to a validator
* Unbonding period - time required in the unbonding state when unbonding
shares. Time slashable to old validator after a redelegation. Time for which
validators can be slashed after an infraction. To provide the requisite
cryptoeconomic security guarantees, all of these must be equal.
* Atom provisions - The process of increasing the Atom supply. Atoms are
periodically created on the Cosmos Hub and issued to bonded Atom holders.
The goal of inflation is to incentize most of the Atoms in existence to be
bonded. Atoms are distributed unbonded and using the fee_distribution mechanism
* Transaction fees - transaction fee is a fee that is included in a Cosmsos Hub
transaction. The fees are collected by the current validator set and
distributed among validators and delegators in proportion to their bonded
Atom share
* Commission fee - a fee taken from the transaction fees by a validator for
their service
## The pool and the share
At the core of the Staking module is the concept of a pool which denotes a
collection of Atoms contributed by different Atom holders. There are three
pools in the Staking module: the bonded, unbonding, and unbonded pool. Bonded
Atoms are part of the global bonded pool. If a validator or delegator wants to
unbond its shares, these Shares are moved to the the unbonding pool for the
duration of the unbonding period. From here normally Atoms will be moved
directly into the delegators wallet, however under the situation thatn an
entire validator gets unbonded, the Atoms of the delegations will remain with
the validator and moved to the unbonded pool. For each pool, the total amount
of bonded, unbonding, or unbonded Atoms are tracked as well as the current
amount of issued pool-shares, the specific holdings of these shares by
validators are tracked in protocol by the validator object.
A share is a unit of Atom distribution and the value of the share
(share-to-atom exchange rate) can change during system execution. The
share-to-atom exchange rate can be computed as:
`share-to-atom-exchange-rate = size of the pool / ammount of issued shares`
Then for each validator (in a per validator data structure) the protocol keeps
track of the amount of shares the validator owns in a pool. At any point in
time, the exact amount of Atoms a validator has in the pool can be computed as
the number of shares it owns multiplied with the current share-to-atom exchange
rate:
`validator-coins = validator.Shares * share-to-atom-exchange-rate`
The benefit of such accounting of the pool resources is the fact that a
modification to the pool from bonding/unbonding/slashing of Atoms affects only
global data (size of the pool and the number of shares) and not the related
validator data structure, i.e., the data structure of other validators do not
need to be modified. This has the advantage that modifying global data is much
cheaper computationally than modifying data of every validator. Let's explain
this further with several small examples:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXX TODO make way less verbose lets use bullet points to describe the example
XXX Also need to update to not include bonded atom provisions all atoms are
XXX redistributed with the fee pool now
We consider initially 4 validators p1, p2, p3 and p4, and that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 shares (note that the initial distribution of the shares,
i.e., share-to-atom exchange rate can be set to any meaningful value), i.e.,
share-to-atom-ex-rate = 1 atom per share. Then at the global pool level we
have, the size of the pool is 40 Atoms, and the amount of issued shares is
equal to 40. And for each validator we store in their corresponding data
structure that each has 10 shares of the bonded pool. Now lets assume that the
validator p4 starts process of unbonding of 5 shares. Then the total size of
the pool is decreased and now it will be 35 shares and the amount of Atoms is
35 . Note that the only change in other data structures needed is reducing the
number of shares for a validator p4 from 10 to 5.
Let's consider now the case where a validator p1 wants to bond 15 more atoms to
the pool. Now the size of the pool is 50, and as the exchange rate hasn't
changed (1 share is still worth 1 Atom), we need to create more shares, i.e. we
now have 50 shares in the pool in total. Validators p2, p3 and p4 still have
(correspondingly) 10, 10 and 5 shares each worth of 1 atom per share, so we
don't need to modify anything in their corresponding data structures. But p1
now has 25 shares, so we update the amount of shares owned by p1 in its
data structure. Note that apart from the size of the pool that is in Atoms, all
other data structures refer only to shares.
Finally, let's consider what happens when new Atoms are created and added to
the pool due to inflation. Let's assume that the inflation rate is 10 percent
and that it is applied to the current state of the pool. This means that 5
Atoms are created and added to the pool and that each validator now
proportionally increase it's Atom count. Let's analyse how this change is
reflected in the data structures. First, the size of the pool is increased and
is now 55 atoms. As a share of each validator in the pool hasn't changed, this
means that the total number of shares stay the same (50) and that the amount of
shares of each validator stays the same (correspondingly 25, 10, 10, 5). But
the exchange rate has changed and each share is now worth 55/50 Atoms per
share, so each validator has effectively increased amount of Atoms it has. So
validators now have (correspondingly) 55/2, 55/5, 55/5 and 55/10 Atoms.
The concepts of the pool and its shares is at the core of the accounting in the
Staking module. It is used for managing the global pools (such as bonding and
unbonding pool), but also for distribution of Atoms between validator and its
delegators (we will explain this in section X).
#### Delegator shares
A validator is, depending on its status, contributing Atoms to either the
unbonding or unbonded pool - the validator in turn holds some amount of pool
shares. Not all of a validator's Atoms (and respective shares) are necessarily
owned by the validator, some may be owned by delegators to that validator. The
mechanism for distribution of Atoms (and shares) between a validator and its
delegators is based on a notion of delegator shares. More precisely, every
validator is issuing (local) delegator shares
(`Validator.IssuedDelegatorShares`) that represents some portion of global
shares managed by the validator (`Validator.GlobalStakeShares`). The principle
behind managing delegator shares is the same as described in [Section](#The
pool and the share). We now illustrate it with an example.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXX TODO make way less verbose lets use bullet points to describe the example
XXX Also need to update to not include bonded atom provisions all atoms are
XXX redistributed with the fee pool now
Let's consider 4 validators p1, p2, p3 and p4, and assume that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 global shares, i.e., that
`share-to-atom-exchange-rate = 1 atom per share`. So we will set
`GlobalState.BondedPool = 40` and `GlobalState.BondedShares = 40` and in the
Validator data structure of each validator `Validator.GlobalStakeShares = 10`.
Furthermore, each validator issued 10 delegator shares which are initially
owned by itself, i.e., `Validator.IssuedDelegatorShares = 10`, where
`delegator-share-to-global-share-ex-rate = 1 global share per delegator share`.
Now lets assume that a delegator d1 delegates 5 atoms to a validator p1 and
consider what are the updates we need to make to the data structures. First,
`GlobalState.BondedPool = 45` and `GlobalState.BondedShares = 45`. Then, for
validator p1 we have `Validator.GlobalStakeShares = 15`, but we also need to
issue also additional delegator shares, i.e.,
`Validator.IssuedDelegatorShares = 15` as the delegator d1 now owns 5 delegator
shares of validator p1, where each delegator share is worth 1 global shares,
i.e, 1 Atom. Lets see now what happens after 5 new Atoms are created due to
inflation. In that case, we only need to update `GlobalState.BondedPool` which
is now equal to 50 Atoms as created Atoms are added to the bonded pool. Note
that the amount of global and delegator shares stay the same but they are now
worth more as share-to-atom-exchange-rate is now worth 50/45 Atoms per share.
Therefore, a delegator d1 now owns:
`delegatorCoins = 5 (delegator shares) * 1 (delegator-share-to-global-share-ex-rate) * 50/45 (share-to-atom-ex-rate) = 5.55 Atoms`

View File

@ -0,0 +1,94 @@
# Testnet Setup
**Note:** This document is incomplete and may not be up-to-date with the
state of the code.
See the [installation guide](../sdk/install.html) for details on
installation.
Here is a quick example to get you off your feet:
First, generate a couple of genesis transactions to be incorporated into
the genesis file, this will create two keys with the password
`1234567890`:
```
gaiad init gen-tx --name=foo --home=$HOME/.gaiad1
gaiad init gen-tx --name=bar --home=$HOME/.gaiad2
gaiacli keys list
```
**Note:** If you've already run these tests you may need to overwrite
keys using the `--owk` flag When you list the keys you should see two
addresses, we'll need these later so take note. Now let's actually
create the genesis files for both nodes:
```
cp -a ~/.gaiad2/config/gentx/. ~/.gaiad1/config/gentx/
cp -a ~/.gaiad1/config/gentx/. ~/.gaiad2/config/gentx/
gaiad init --gen-txs --home=$HOME/.gaiad1 --chain-id=test-chain
gaiad init --gen-txs --home=$HOME/.gaiad2 --chain-id=test-chain
```
**Note:** If you've already run these tests you may need to overwrite
genesis using the `-o` flag. What we just did is copy the genesis
transactions between each of the nodes so there is a common genesis
transaction set; then we created both genesis files independently from
each home directory. Importantly both nodes have independently created
their `genesis.json` and `config.toml` files, which should be identical
between nodes.
Great, now that we've initialized the chains, we can start both nodes in
the background:
```
gaiad start --home=$HOME/.gaiad1 &> gaia1.log &
NODE1_PID=$!
gaia start --home=$HOME/.gaiad2 &> gaia2.log &
NODE2_PID=$!
```
Note that we save the PID so we can later kill the processes. You can
peak at your logs with `tail gaia1.log`, or follow them for a bit with
`tail -f gaia1.log`.
Nice. We can also lookup the validator set:
```
gaiacli validatorset
```
Then, we try to transfer some `steak` to another account:
```
gaiacli account <FOO-ADDR>
gaiacli account <BAR-ADDR>
gaiacli send --amount=10steak --to=<BAR-ADDR> --name=foo --chain-id=test-chain
```
**Note:** We need to be careful with the `chain-id` and `sequence`
Check the balance & sequence with:
```
gaiacli account <BAR-ADDR>
```
To confirm for certain the new validator is active, check tendermint:
```
curl localhost:46657/validators
```
Finally, to relinquish all your power, unbond some coins. You should see
your VotingPower reduce and your account balance increase.
```
gaiacli unbond --chain-id=<chain-id> --name=test
```
That's it!
**Note:** TODO demonstrate edit-candidacy **Note:** TODO demonstrate
delegation **Note:** TODO demonstrate unbond of delegation **Note:**
TODO demonstrate unbond candidate

View File

@ -66,7 +66,7 @@ To confirm for certain the new validator is active, check tendermint:
::
curl localhost:46657/validators
curl localhost:26657/validators
Finally, to relinquish all your power, unbond some coins. You should see your VotingPower reduce and your account balance increase.

View File

@ -17,8 +17,9 @@ SDK
.. toctree::
:maxdepth: 1
sdk/install.rst
sdk/key-management.rst
guides/sdk/install.md
guides/sdk/key-management.md
.. sdk/overview.rst # needs to be updated
.. old/glossary.rst # not completely up to date but has good content
@ -47,7 +48,7 @@ Staking
.. toctree::
:maxdepth: 1
staking/testnet.rst
guides/staking/testnet.md
.. staking/intro.rst
.. staking/key-management.rst
.. staking/local-testnet.rst

View File

@ -1,48 +0,0 @@
Install
=======
Cosmos SDK can be installed to
``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program:
::
go get github.com/cosmos/cosmos-sdk
If the dependencies have been updated with breaking changes, or if
another branch is required, ``dep`` is used for dependency management.
Thus, assuming you've already run ``go get`` or otherwise cloned the
repo, the correct way to install is:
::
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
make get_vendor_deps
make install
make install_examples
This will install ``gaiad`` and ``gaiacli`` and four example binaries:
``basecoind``, ``basecli``, ``democoind``, and ``democli``.
Verify that everything is OK by running:
::
gaiad version
you should see:
::
0.15.0-rc1-9d90c6b
then with:
::
gaiacli version
you should see:
::
0.15.0-rc1-9d90c6b

View File

@ -1,18 +0,0 @@
Key Management
==============
Here we cover many aspects of handling keys within the Cosmos SDK framework.
Pseudo Code
-----------
Generating an address for an ed25519 public key (in pseudo code):
::
const TypeDistinguisher = HexToBytes("1624de6220")
// prepend the TypeDistinguisher as Bytes
SerializedBytes = TypeDistinguisher ++ PubKey.asBytes()
Address = ripemd160(SerializedBytes)

View File

@ -7,11 +7,13 @@ NOTE: the specifications are not yet complete and very much a work in progress.
- [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for
sending tokens.
- [Staking](staking) - Proof of Stake related specifications including bonding
and delegation transactions, inflation, fees, etc.
- [Governance](governance) - Governance related specifications including
proposals and voting.
- [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol.
- [Staking](staking) - Proof-of-stake related specifications including bonding
and delegation transactions, inflation, etc.
- [Slashing](slashing) - Specifications of validator punishment mechanisms
- [Provisioning](provisioning) - Fee distribution, and atom provision distribution specification
- [Other](other) - Other components of the Cosmos Hub, including the reserve
pool, All in Bits vesting, etc.

View File

@ -0,0 +1,229 @@
# Fee Distribution
## Overview
Fees are pooled separately and withdrawn lazily, at any time. They are not
bonded, and can be paid in multiple tokens. An adjustment factor is maintained
for each validator and delegator to determine the true proportion of fees in
the pool they are entitled too. Adjustment factors are updated every time a
validator or delegator's voting power changes. Validators and delegators must
withdraw all fees they are entitled too before they can bond or unbond Atoms.
## Affect on Staking
Because fees are optimized to note
Commission on Atom Provisions and having atoms autobonded are mutually
exclusive (we cant have both). The reason for this is that if there are atoms
commissions and autobonding, the portion of atoms the fee distribution
calculation would become very large as the atom portion for each delegator
would change each block making a withdrawal of fees for a delegator require a
calculation for every single block since the last withdrawal. Conclusion we can
only have atom commission and unbonded atoms provisions, or bonded atom
provisions and no atom commission
## Fee Calculations
Collected fees are pooled globally and divided out passively to validators and
delegators. Each validator has the opportunity to charge commission to the
delegators on the fees collected on behalf of the delegators by the validators.
Fees are paid directly into a global fee pool. Due to the nature of of passive
accounting whenever changes to parameters which affect the rate of fee
distribution occurs, withdrawal of fees must also occur.
- when withdrawing one must withdrawal the maximum amount they are entitled
too, leaving nothing in the pool,
- when bonding, unbonding, or re-delegating tokens to an existing account a
full withdrawal of the fees must occur (as the rules for lazy accounting
change),
- when a validator chooses to change the commission on fees, all accumulated
commission fees must be simultaneously withdrawn.
When the validator is the proposer of the round, that validator (and their
delegators) receives between 1% and 5% of fee rewards, the reserve tax is then
charged, then the remainder is distributed socially by voting power to all
validators including the proposer validator. The amount of proposer reward is
calculated from pre-commits Tendermint messages. All provision rewards are
added to a provision reward pool which validator holds individually. Here note
that `BondedShares` represents the sum of all voting power saved in the
`GlobalState` (denoted `gs`).
```
proposerReward = feesCollected * (0.01 + 0.04
* sumOfVotingPowerOfPrecommitValidators / gs.BondedShares)
validator.ProposerRewardPool += proposerReward
reserveTaxed = feesCollected * params.ReserveTax
gs.ReservePool += reserveTaxed
distributedReward = feesCollected - proposerReward - reserveTaxed
gs.FeePool += distributedReward
gs.SumFeesReceived += distributedReward
gs.RecentFee = distributedReward
```
The entitlement to the fee pool held by the each validator can be accounted for
lazily. First we must account for a validator's `count` and `adjustment`. The
`count` represents a lazy accounting of what that validators entitlement to the
fee pool would be if there `VotingPower` was to never change and they were to
never withdraw fees.
```
validator.count = validator.VotingPower * BlockHeight
```
Similarly the GlobalState count can be passively calculated whenever needed,
where `BondedShares` is the updated sum of voting powers from all validators.
```
gs.count = gs.BondedShares * BlockHeight
```
The `adjustment` term accounts for changes in voting power and withdrawals of
fees. The adjustment factor must be persisted with the validator and modified
whenever fees are withdrawn from the validator or the voting power of the
validator changes. When the voting power of the validator changes the
`Adjustment` factor is increased/decreased by the cumulative difference in the
voting power if the voting power has been the new voting power as opposed to
the old voting power for the entire duration of the blockchain up the previous
block. Each time there is an adjustment change the GlobalState (denoted `gs`)
`Adjustment` must also be updated.
```
simplePool = validator.count / gs.count * gs.SumFeesReceived
projectedPool = validator.PrevPower * (height-1)
/ (gs.PrevPower * (height-1)) * gs.PrevFeesReceived
+ validator.Power / gs.Power * gs.RecentFee
AdjustmentChange = simplePool - projectedPool
validator.AdjustmentRewardPool += AdjustmentChange
gs.Adjustment += AdjustmentChange
```
Every instance that the voting power changes, information about the state of
the validator set during the change must be recorded as a `powerChange` for
other validators to run through. Before any validator modifies its voting power
it must first run through the above calculation to determine the change in
their `caandidate.AdjustmentRewardPool` for all historical changes in the set
of `powerChange` which they have not yet synced to. The set of all
`powerChange` may be trimmed from its oldest members once all validators have
synced past the height of the oldest `powerChange`. This trim procedure will
occur on an epoch basis.
```golang
type powerChange struct {
height int64 // block height at change
power rational.Rat // total power at change
prevpower rational.Rat // total power at previous height-1
feesin coins.Coin // fees in at block height
prevFeePool coins.Coin // total fees in at previous block height
}
```
Note that the adjustment factor may result as negative if the voting power of a
different validator has decreased.
```
validator.AdjustmentRewardPool += withdrawn
gs.Adjustment += withdrawn
```
Now the entitled fee pool of each validator can be lazily accounted for at
any given block:
```
validator.feePool = validator.simplePool - validator.Adjustment
```
So far we have covered two sources fees which can be withdrawn from: Fees from
proposer rewards (`validator.ProposerRewardPool`), and fees from the fee pool
(`validator.feePool`). However we should note that all fees from fee pool are
subject to commission rate from the owner of the validator. These next
calculations outline the math behind withdrawing fee rewards as either a
delegator to a validator providing commission, or as the owner of a validator
who is receiving commission.
### Calculations For Delegators and Validators
The same mechanism described to calculate the fees which an entire validator is
entitled to is be applied to delegator level to determine the entitled fees for
each delegator and the validators entitled commission from `gs.FeesPool` and
`validator.ProposerRewardPool`.
The calculations are identical with a few modifications to the parameters:
- Delegator's entitlement to `gs.FeePool`:
- entitled party voting power should be taken as the effective voting power
after commission is retrieved,
`bond.Shares/validator.TotalDelegatorShares * validator.VotingPower * (1 - validator.Commission)`
- Delegator's entitlement to `validator.ProposerFeePool`
- global power in this context is actually shares
`validator.TotalDelegatorShares`
- entitled party voting power should be taken as the effective shares after
commission is retrieved, `bond.Shares * (1 - validator.Commission)`
- Validator's commission entitlement to `gs.FeePool`
- entitled party voting power should be taken as the effective voting power
of commission portion of total voting power,
`validator.VotingPower * validator.Commission`
- Validator's commission entitlement to `validator.ProposerFeePool`
- global power in this context is actually shares
`validator.TotalDelegatorShares`
- entitled party voting power should be taken as the of commission portion
of total delegators shares,
`validator.TotalDelegatorShares * validator.Commission`
For more implementation ideas see spreadsheet `spec/AbsoluteFeeDistrModel.xlsx`
As mentioned earlier, every time the voting power of a delegator bond is
changing either by unbonding or further bonding, all fees must be
simultaneously withdrawn. Similarly if the validator changes the commission
rate, all commission on fees must be simultaneously withdrawn.
### Other general notes on fees accounting
- When a delegator chooses to re-delegate shares, fees continue to accumulate
until the re-delegation queue reaches maturity. At the block which the queue
reaches maturity and shares are re-delegated all available fees are
simultaneously withdrawn.
- Whenever a totally new validator is added to the validator set, the `accum`
of the entire validator must be 0, meaning that the initial value for
`validator.Adjustment` must be set to the value of `canidate.Count` for the
height which the validator is added on the validator set.
- The feePool of a new delegator bond will be 0 for the height at which the bond
was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to
the height which the bond was added.
### Atom provisions
Validator provisions are minted on an hourly basis (the first block of a new
hour). The annual target of between 7% and 20%. The long-term target ratio of
bonded tokens to unbonded tokens is 67%.
The target annual inflation rate is recalculated for each provisions cycle. The
inflation is also subject to a rate change (positive or negative) depending on
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
```go
inflationRateChange(0) = 0
Inflation(0) = 0.07
bondedRatio = Pool.BondedTokens / Pool.TotalSupplyTokens
AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13
annualInflation += AnnualInflationRateChange
if annualInflation > 0.20 then Inflation = 0.20
if annualInflation < 0.07 then Inflation = 0.07
provisionTokensHourly = Pool.TotalSupplyTokens * Inflation / (365.25*24)
```
Because the validators hold a relative bonded share (`GlobalStakeShares`), when
more bonded tokens are added proportionally to all validators, the only term
which needs to be updated is the `GlobalState.BondedPool`. So for each
provisions cycle:
```go
Pool.BondedPool += provisionTokensHourly
```

View File

@ -0,0 +1,13 @@
Validator
* Adjustment factor used to passively calculate each validators entitled fees
from `GlobalState.FeePool`
Delegation Shares
* AdjustmentFeePool: Adjustment factor used to passively calculate each bonds
entitled fees from `GlobalState.FeePool`
* AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Validator.ProposerRewardPool`

View File

@ -0,0 +1,19 @@
### TxProveLive
If a validator was automatically unbonded due to liveness issues and wishes to
assert it is still online, it can send `TxProveLive`:
```golang
type TxProveLive struct {
PubKey crypto.PubKey
}
```
All delegators in the temporary unbonding pool which have not
transacted to move will be bonded back to the now-live validator and begin to
once again collect provisions and rewards.
```
TODO: pseudo-code
```

View File

@ -0,0 +1,100 @@
# Validator Set Changes
## Slashing
Messges which may compromise the safety of the underlying consensus protocol ("equivocations")
result in some amount of the offending validator's shares being removed ("slashed").
Currently, such messages include only the following:
- prevotes by the same validator for more than one BlockID at the same
Height and Round
- precommits by the same validator for more than one BlockID at the same
Height and Round
We call any such pair of conflicting votes `Evidence`. Full nodes in the network prioritize the
detection and gossipping of `Evidence` so that it may be rapidly included in blocks and the offending
validators punished.
For some `evidence` to be valid, it must satisfy:
`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE`
where `evidence.Timestamp` is the timestamp in the block at height
`evidence.Height` and `block.Timestamp` is the current block timestamp.
If valid evidence is included in a block, the offending validator loses
a constant `SLASH_PROPORTION` of their current stake at the beginning of the block:
```
oldShares = validator.shares
validator.shares = oldShares * (1 - SLASH_PROPORTION)
```
This ensures that offending validators are punished the same amount whether they
act as a single validator with X stake or as N validators with collectively X
stake.
## Automatic Unbonding
Every block includes a set of precommits by the validators for the previous block,
known as the LastCommit. A LastCommit is valid so long as it contains precommits from +2/3 of voting power.
Proposers are incentivized to include precommits from all
validators in the LastCommit by receiving additional fees
proportional to the difference between the voting power included in the
LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)).
Validators are penalized for failing to be included in the LastCommit for some
number of blocks by being automatically unbonded.
The following information is stored with each validator candidate, and is only non-zero if the candidate becomes an active validator:
```go
type ValidatorSigningInfo struct {
StartHeight int64
IndexOffset int64
JailedUntil int64
SignedBlocksCounter int64
SignedBlocksBitArray BitArray
}
```
Where:
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
* `JailedUntil` is set whenever the candidate is revoked due to downtime
* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always.
* `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks,
whether or not this validator was included in the LastCommit. It uses a `1` if the validator was included, and a `0` if it was not. Note it is initialized with all 0s.
At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded:
```
height := block.Height
for val in block.Validators:
signInfo = val.SignInfo
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
signInfo.IndexOffset++
previous = signInfo.SignedBlocksBitArray.Get(index)
// update counter if array has changed
if previous and val in block.AbsentValidators:
signInfo.SignedBlocksBitArray.Set(index, 0)
signInfo.SignedBlocksCounter--
else if !previous and val not in block.AbsentValidators:
signInfo.SignedBlocksBitArray.Set(index, 1)
signInfo.SignedBlocksCounter++
// else previous == val not in block.AbsentValidators, no change
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
slash & unbond the validator
```

View File

@ -2,30 +2,38 @@
## Abstract
This paper specifies the Staking module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
This paper specifies the Staking module of the Cosmos-SDK, which was first
described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper)
in June 2016.
The module enables Cosmos-SDK based blockchain to support an advanced Proof-of-Stake system. In this system, holders of the native staking token of the chain can become candidate validators and can delegate tokens to candidate validators, ultimately determining the effective validator set for the system.
The module enables Cosmos-SDK based blockchain to support an advanced
Proof-of-Stake system. In this system, holders of the native staking token of
the chain can become validators and can delegate tokens to validator
validators, ultimately determining the effective validator set for the system.
This module will be used in the Cosmos Hub, the first Hub in the Cosmos network.
This module will be used in the Cosmos Hub, the first Hub in the Cosmos
network.
## Contents
The following specification uses *Atom* as the native staking token. The module can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the native staking token of the chain.
The following specification uses *Atom* as the native staking token. The module
can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the
native staking token of the chain.
1. **[Design overview](overview.md)**
2. **Implementation**
1. **[State](state.md)**
1. Global State
2. Validator Candidates
3. Delegator Bonds
4. Unbond and Rebond Queue
1. Params
1. Pool
2. Validators
3. Delegations
2. **[Transactions](transactions.md)**
1. Declare Candidacy
2. Edit Candidacy
3. Delegate
4. Unbond
5. Redelegate
6. ProveLive
1. Create-Validator
2. Edit-Validator
3. Repeal-Revocation
4. Delegate
5. Unbond
6. Redelegate
3. **[Validator Set Changes](valset-changes.md)**
1. Validator set updates
2. Slashing

View File

@ -0,0 +1,61 @@
# End-Block
Two staking activities are intended to be processed in the application end-block.
- inform Tendermint of validator set changes
- process and set atom inflation
# Validator Set Changes
The Tendermint validator set may be updated by state transitions that run at
the end of every block. The Tendermint validator set may be changed by
validators either being revoked due to inactivity/unexpected behaviour (covered
in slashing) or changed in validator power. Determining which validator set
changes must be made occurs during staking transactions (and slashing
transactions) - during end-block the already accounted changes are applied and
the changes cleared
```golang
EndBlock() ValidatorSetChanges
vsc = GetTendermintUpdates()
ClearTendermintUpdates()
return vsc
```
# Inflation
The atom inflation rate is changed once per hour based on the current and
historic bond ratio
```golang
processProvisions():
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
time = BFTTime()
if time > pool.InflationLastTime + ProvisionTimeout
pool.InflationLastTime = time
pool.Inflation = nextInflation(hrsPerYr).Round(1000000000)
provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr)
pool.LooseUnbondedTokens += provisions
feePool += LooseUnbondedTokens
setPool(pool)
nextInflation(hrsPerYr rational.Rat):
if pool.TotalSupply > 0
bondedRatio = pool.BondedPool / pool.TotalSupply
else
bondedRation = 0
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear / hrsPerYr
inflation = pool.Inflation + inflationRateChange
if inflation > params.InflationMax then inflation = params.InflationMax
if inflation < params.InflationMin then inflation = params.InflationMin
return inflation
```

View File

@ -1,214 +0,0 @@
# Staking Module
## Overview
The Cosmos Hub is a Tendermint-based Proof of Stake blockchain system that
serves as a backbone of the Cosmos ecosystem. It is operated and secured by an
open and globally decentralized set of validators. Tendermint consensus is a
Byzantine fault-tolerant distributed protocol that involves all validators in
the process of exchanging protocol messages in the production of each block. To
avoid Nothing-at-Stake problem, a validator in Tendermint needs to lock up
coins in a bond deposit. Tendermint protocol messages are signed by the
validator's private key, and this is a basis for Tendermint strict
accountability that allows punishing misbehaving validators by slashing
(burning) their bonded Atoms. On the other hand, validators are rewarded for
their service of securing blockchain network by the inflationary provisions and
transactions fees. This incentives correct behavior of the validators and
provides the economic security of the network.
The native token of the Cosmos Hub is called Atom; becoming a validator of the
Cosmos Hub requires holding Atoms. However, not all Atom holders are validators
of the Cosmos Hub. More precisely, there is a selection process that determines
the validator set as a subset of all validator candidates (Atom holders that
wants to become a validator). The other option for Atom holder is to delegate
their atoms to validators, i.e., being a delegator. A delegator is an Atom
holder that has bonded its Atoms by delegating it to a validator (or validator
candidate). By bonding Atoms to secure the network (and taking a risk of being
slashed in case of misbehaviour), a user is rewarded with inflationary
provisions and transaction fees proportional to the amount of its bonded Atoms.
The Cosmos Hub is designed to efficiently facilitate a small numbers of
validators (hundreds), and large numbers of delegators (tens of thousands).
More precisely, it is the role of the Staking module of the Cosmos Hub to
support various staking functionality including validator set selection,
delegating, bonding and withdrawing Atoms, and the distribution of inflationary
provisions and transaction fees.
## Basic Terms and Definitions
* Cosmsos Hub - a Tendermint-based Proof of Stake blockchain system
* Atom - native token of the Cosmsos Hub
* Atom holder - an entity that holds some amount of Atoms
* Candidate - an Atom holder that is actively involved in the Tendermint
blockchain protocol (running Tendermint Full Node (TODO: add link to Full
Node definition) and is competing with other candidates to be elected as a
validator (TODO: add link to Validator definition))
* Validator - a candidate that is currently selected among a set of candidates
to be able to sign protocol messages in the Tendermint consensus protocol
* Delegator - an Atom holder that has bonded some of its Atoms by delegating
them to a validator (or a candidate)
* Bonding Atoms - a process of locking Atoms in a bond deposit (putting Atoms
under protocol control). Atoms are always bonded through a validator (or
candidate) process. Bonded atoms can be slashed (burned) in case a validator
process misbehaves (does not behave according to the protocol specification).
Atom holders can regain access to their bonded Atoms if they have not been
slashed by waiting an Unbonding period.
* Unbonding period - a period of time after which Atom holder gains access to
its bonded Atoms (they can be withdrawn to a user account) or they can be
re-delegated.
* Inflationary provisions - inflation is the process of increasing the Atom supply.
Atoms are periodically created on the Cosmos Hub and issued to bonded Atom holders.
The goal of inflation is to incentize most of the Atoms in existence to be bonded.
* Transaction fees - transaction fee is a fee that is included in a Cosmsos Hub
transaction. The fees are collected by the current validator set and
distributed among validators and delegators in proportion to their bonded
Atom share.
* Commission fee - a fee taken from the transaction fees by a validator for
their service
## The pool and the share
At the core of the Staking module is the concept of a pool which denotes a
collection of Atoms contributed by different Atom holders. There are two global
pools in the Staking module: the bonded pool and unbonding pool. Bonded Atoms
are part of the global bonded pool. If a candidate or delegator wants to unbond
its Atoms, those Atoms are moved to the the unbonding pool for the duration of
the unbonding period. In the Staking module, a pool is a logical concept, i.e.,
there is no pool data structure that would be responsible for managing pool
resources. Instead, it is managed in a distributed way. More precisely, at the
global level, for each pool, we track only the total amount of bonded or unbonded
Atoms and the current amount of issued shares. A share is a unit of Atom distribution
and the value of the share (share-to-atom exchange rate) changes during
system execution. The share-to-atom exchange rate can be computed as:
`share-to-atom-exchange-rate = size of the pool / ammount of issued shares`
Then for each validator candidate (in a per candidate data structure) we keep track of
the amount of shares the candidate owns in a pool. At any point in time,
the exact amount of Atoms a candidate has in the pool can be computed as the
number of shares it owns multiplied with the current share-to-atom exchange rate:
`candidate-coins = candidate.Shares * share-to-atom-exchange-rate`
The benefit of such accounting of the pool resources is the fact that a
modification to the pool from bonding/unbonding/slashing/provisioning of
Atoms affects only global data (size of the pool and the number of shares) and
not the related validator/candidate data structure, i.e., the data structure of
other validators do not need to be modified. This has the advantage that
modifying global data is much cheaper computationally than modifying data of
every validator. Let's explain this further with several small examples:
We consider initially 4 validators p1, p2, p3 and p4, and that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 shares (note that the initial distribution of the shares,
i.e., share-to-atom exchange rate can be set to any meaningful value), i.e.,
share-to-atom-ex-rate = 1 atom per share. Then at the global pool level we
have, the size of the pool is 40 Atoms, and the amount of issued shares is
equal to 40. And for each validator we store in their corresponding data
structure that each has 10 shares of the bonded pool. Now lets assume that the
validator p4 starts process of unbonding of 5 shares. Then the total size of
the pool is decreased and now it will be 35 shares and the amount of Atoms is
35 . Note that the only change in other data structures needed is reducing the
number of shares for a validator p4 from 10 to 5.
Let's consider now the case where a validator p1 wants to bond 15 more atoms to
the pool. Now the size of the pool is 50, and as the exchange rate hasn't
changed (1 share is still worth 1 Atom), we need to create more shares, i.e. we
now have 50 shares in the pool in total. Validators p2, p3 and p4 still have
(correspondingly) 10, 10 and 5 shares each worth of 1 atom per share, so we
don't need to modify anything in their corresponding data structures. But p1
now has 25 shares, so we update the amount of shares owned by p1 in its
data structure. Note that apart from the size of the pool that is in Atoms, all
other data structures refer only to shares.
Finally, let's consider what happens when new Atoms are created and added to
the pool due to inflation. Let's assume that the inflation rate is 10 percent
and that it is applied to the current state of the pool. This means that 5
Atoms are created and added to the pool and that each validator now
proportionally increase it's Atom count. Let's analyse how this change is
reflected in the data structures. First, the size of the pool is increased and
is now 55 atoms. As a share of each validator in the pool hasn't changed, this
means that the total number of shares stay the same (50) and that the amount of
shares of each validator stays the same (correspondingly 25, 10, 10, 5). But
the exchange rate has changed and each share is now worth 55/50 Atoms per
share, so each validator has effectively increased amount of Atoms it has. So
validators now have (correspondingly) 55/2, 55/5, 55/5 and 55/10 Atoms.
The concepts of the pool and its shares is at the core of the accounting in the
Staking module. It is used for managing the global pools (such as bonding and
unbonding pool), but also for distribution of Atoms between validator and its
delegators (we will explain this in section X).
#### Delegator shares
A candidate is, depending on it's status, contributing Atoms to either the
bonded or unbonding pool, and in return gets some amount of (global) pool
shares. Note that not all those Atoms (and respective shares) are owned by the
candidate as some Atoms could be delegated to a candidate. The mechanism for
distribution of Atoms (and shares) between a candidate and it's delegators is
based on a notion of delegator shares. More precisely, every candidate is
issuing (local) delegator shares (`Candidate.IssuedDelegatorShares`) that
represents some portion of global shares managed by the candidate
(`Candidate.GlobalStakeShares`). The principle behind managing delegator shares
is the same as described in [Section](#The pool and the share). We now
illustrate it with an example.
Let's consider 4 validators p1, p2, p3 and p4, and assume that each validator
has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have
issued initially 40 global shares, i.e., that
`share-to-atom-exchange-rate = 1 atom per share`. So we will set
`GlobalState.BondedPool = 40` and `GlobalState.BondedShares = 40` and in the
Candidate data structure of each validator `Candidate.GlobalStakeShares = 10`.
Furthermore, each validator issued 10 delegator shares which are initially
owned by itself, i.e., `Candidate.IssuedDelegatorShares = 10`, where
`delegator-share-to-global-share-ex-rate = 1 global share per delegator share`.
Now lets assume that a delegator d1 delegates 5 atoms to a validator p1 and
consider what are the updates we need to make to the data structures. First,
`GlobalState.BondedPool = 45` and `GlobalState.BondedShares = 45`. Then, for
validator p1 we have `Candidate.GlobalStakeShares = 15`, but we also need to
issue also additional delegator shares, i.e.,
`Candidate.IssuedDelegatorShares = 15` as the delegator d1 now owns 5 delegator
shares of validator p1, where each delegator share is worth 1 global shares,
i.e, 1 Atom. Lets see now what happens after 5 new Atoms are created due to
inflation. In that case, we only need to update `GlobalState.BondedPool` which
is now equal to 50 Atoms as created Atoms are added to the bonded pool. Note
that the amount of global and delegator shares stay the same but they are now
worth more as share-to-atom-exchange-rate is now worth 50/45 Atoms per share.
Therefore, a delegator d1 now owns:
`delegatorCoins = 5 (delegator shares) * 1 (delegator-share-to-global-share-ex-rate) * 50/45 (share-to-atom-ex-rate) = 5.55 Atoms`
### Inflation provisions
Validator provisions are minted on an hourly basis (the first block of a new
hour). The annual target of between 7% and 20%. The long-term target ratio of
bonded tokens to unbonded tokens is 67%.
The target annual inflation rate is recalculated for each provisions cycle. The
inflation is also subject to a rate change (positive or negative) depending on
the distance from the desired ratio (67%). The maximum rate change possible is
defined to be 13% per year, however the annual inflation is capped as between
7% and 20%.
```go
inflationRateChange(0) = 0
GlobalState.Inflation(0) = 0.07
bondedRatio = GlobalState.BondedPool / GlobalState.TotalSupply
AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13
annualInflation += AnnualInflationRateChange
if annualInflation > 0.20 then GlobalState.Inflation = 0.20
if annualInflation < 0.07 then GlobalState.Inflation = 0.07
provisionTokensHourly = GlobalState.TotalSupply * GlobalState.Inflation / (365.25*24)
```
Because the validators hold a relative bonded share (`GlobalStakeShares`), when
more bonded tokens are added proportionally to all validators, the only term
which needs to be updated is the `GlobalState.BondedPool`. So for each
provisions cycle:
```go
GlobalState.BondedPool += provisionTokensHourly
```

View File

@ -1,204 +1,160 @@
## State
The staking module persists the following information to the store:
* `GlobalState`, a struct describing the global pools, inflation, and
fees
* `ValidatorCandidates: <pubkey | shares> => <candidate>`, a map of all candidates (including current validators) in the store,
indexed by their public key and shares in the global pool.
* `DelegatorBonds: < delegator-address | candidate-pubkey > => <delegator-bond>`. a map of all delegations by a delegator to a candidate,
indexed by delegator address and candidate pubkey.
public key
* `UnbondQueue`, the queue of unbonding delegations
* `RedelegateQueue`, the queue of re-delegations
### Pool
- index: n/a single-record
### Global State
The pool is a space for all dynamic global state of the Cosmos Hub. It tracks
information about the total amounts of Atoms in all states, representative
validator shares for stake in the global pools, moving Atom inflation
information, etc.
The GlobalState contains information about the total amount of Atoms, the
global bonded/unbonded position, the Atom inflation rate, and the fees.
- stored object:
`Params` is global data structure that stores system parameters and defines overall functioning of the
module.
```golang
type Pool struct {
LooseUnbondedTokens int64 // tokens not associated with any validator
UnbondedTokens int64 // reserve of unbonded tokens held with validators
UnbondingTokens int64 // tokens moving from bonded to unbonded pool
BondedTokens int64 // reserve of bonded tokens
UnbondedShares sdk.Rat // sum of all shares distributed for the Unbonded Pool
UnbondingShares sdk.Rat // shares moving from Bonded to Unbonded Pool
BondedShares sdk.Rat // sum of all shares distributed for the Bonded Pool
InflationLastTime int64 // block which the last inflation was processed // TODO make time
Inflation sdk.Rat // current annual inflation rate
``` go
type GlobalState struct {
TotalSupply int64 // total supply of Atoms
BondedPool int64 // reserve of bonded tokens
BondedShares rational.Rat // sum of all shares distributed for the BondedPool
UnbondedPool int64 // reserve of unbonding tokens held with candidates
UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool
InflationLastTime int64 // timestamp of last processing of inflation
Inflation rational.Rat // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset
FeePool coin.Coins // fee pool for all the fee shares which have already been distributed
ReservePool coin.Coins // pool of reserve taxes collected on all fees for governance use
Adjustment rational.Rat // Adjustment factor for calculating global fee accum
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
}
type Params struct {
HoldBonded Address // account where all bonded coins are held
HoldUnbonding Address // account where all delegated but unbonding coins are held
InflationRateChange rational.Rational // maximum annual change in inflation rate
InflationMax rational.Rational // maximum inflation rate
InflationMin rational.Rational // minimum inflation rate
GoalBonded rational.Rational // Goal of percent bonded atoms
ReserveTax rational.Rational // Tax collected on all fees
MaxVals uint16 // maximum number of validators
AllowedBondDenom string // bondable coin denomination
// gas costs for txs
GasDeclareCandidacy int64
GasEditCandidacy int64
GasDelegate int64
GasRedelegate int64
GasUnbond int64
type PoolShares struct {
Status sdk.BondStatus // either: unbonded, unbonding, or bonded
Amount sdk.Rat // total shares of type ShareKind
}
```
### Candidate
### Params
- index: n/a single-record
The `Candidate` holds the current state and some historical
actions of validators or candidate-validators.
Params is global data structure that stores system parameters and defines
overall functioning of the stake module.
``` go
type CandidateStatus byte
- stored object:
const (
Bonded CandidateStatus = 0x01
Unbonded CandidateStatus = 0x02
Revoked CandidateStatus = 0x03
)
```golang
type Params struct {
InflationRateChange sdk.Rat // maximum annual change in inflation rate
InflationMax sdk.Rat // maximum inflation rate
InflationMin sdk.Rat // minimum inflation rate
GoalBonded sdk.Rat // Goal of percent bonded atoms
type Candidate struct {
Status CandidateStatus
ConsensusPubKey crypto.PubKey
GovernancePubKey crypto.PubKey
Owner crypto.Address
GlobalStakeShares rational.Rat
IssuedDelegatorShares rational.Rat
RedelegatingShares rational.Rat
VotingPower rational.Rat
Commission rational.Rat
CommissionMax rational.Rat
CommissionChangeRate rational.Rat
CommissionChangeToday rational.Rat
ProposerRewardPool coin.Coins
Adjustment rational.Rat
Description Description
MaxValidators uint16 // maximum number of validators
BondDenom string // bondable coin denomination
}
```
### Validator
- index 1: validator owner address
- index 2: validator Tendermint PubKey
- index 3: bonded validators only
- index 4: voting power
Related Store which holds Validator.ABCIValidator()
- index: validator owner address
The `Validator` holds the current state and some historical actions of the
validator.
- stored object:
```golang
type Validator struct {
Owner sdk.Address // sender of BondTx - UnbondTx returns here
ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator
Revoked bool // has the validator been revoked?
PoolShares PoolShares // total shares for tokens held in the pool
DelegatorShares sdk.Rat // total shares issued to a validator's delegators
SlashRatio sdk.Rat // increases each time the validator is slashed
Description Description // description terms for the validator
BondHeight int64 // earliest height as a bonded validator
BondIntraTxCounter int16 // block-local tx index of validator change
ProposerRewardPool sdk.Coins // reward pool collected from being the proposer
Commission sdk.Rat // the commission rate of fees charged to any delegators
CommissionMax sdk.Rat // maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Rat // maximum daily increase of the validator commission
CommissionChangeToday sdk.Rat // commission rate change today, reset each day (UTC time)
PrevPoolShares PoolShares // total shares of a global hold pools
}
type Description struct {
Name string
DateBonded string
Identity string
Website string
Details string
Moniker string // name
Identity string // optional identity signature (ex. UPort or Keybase)
Website string // optional website link
Details string // optional details
}
```
Candidate parameters are described:
* Status: it can be Bonded (active validator), Unbonding (validator candidate)
or Revoked
* ConsensusPubKey: candidate public key that is used strictly for participating in
consensus
* GovernancePubKey: public key used by the validator for governance voting
* Owner: Address that is allowed to unbond coins.
* GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if
`Candidate.Status` is `Bonded`; or shares of `GlobalState.Unbondingt Pool`
otherwise
* IssuedDelegatorShares: Sum of all shares a candidate issued to delegators
(which includes the candidate's self-bond); a delegator share represents
their stake in the Candidate's `GlobalStakeShares`
* RedelegatingShares: The portion of `IssuedDelegatorShares` which are
currently re-delegating to a new validator
* VotingPower: Proportional to the amount of bonded tokens which the validator
has if `Candidate.Status` is `Bonded`; otherwise it is equal to `0`
* Commission: The commission rate of fees charged to any delegators
* CommissionMax: The maximum commission rate this candidate can charge each
day from the date `GlobalState.DateLastCommissionReset`
* CommissionChangeRate: The maximum daily increase of the candidate commission
* CommissionChangeToday: Counter for the amount of change to commission rate
which has occurred today, reset on the first block of each day (UTC time)
* ProposerRewardPool: reward pool for extra fees collected when this candidate
is the proposer of a block
* Adjustment factor used to passively calculate each validators entitled fees
from `GlobalState.FeePool`
* Description
* Name: moniker
* DateBonded: date determined which the validator was bonded
* Identity: optional field to provide a signature which verifies the
validators identity (ex. UPort or Keybase)
* Website: optional website link
* Details: optional details
### Delegation
- index: delegation address
### DelegatorBond
Atom holders may delegate coins to candidates; under this circumstance their
funds are held in a `DelegatorBond` data structure. It is owned by one
delegator, and is associated with the shares for one candidate. The sender of
Atom holders may delegate coins to validators; under this circumstance their
funds are held in a `Delegation` data structure. It is owned by one
delegator, and is associated with the shares for one validator. The sender of
the transaction is the owner of the bond.
``` go
type DelegatorBond struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
AdjustmentRewardPool coin.Coins
}
```
Description:
* Candidate: the public key of the validator candidate: bonding too
* Shares: the number of delegator shares received from the validator candidate
* AdjustmentFeePool: Adjustment factor used to passively calculate each bonds
entitled fees from `GlobalState.FeePool`
* AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool`
### QueueElem
The Unbonding and re-delegation process is implemented using the ordered queue
data structure. All queue elements share a common structure:
- stored object:
```golang
type QueueElem struct {
Candidate crypto.PubKey
InitTime int64 // when the element was added to the queue
type Delegation struct {
DelegatorAddr sdk.Address // delegation owner address
ValidatorAddr sdk.Address // validator owner address
Shares sdk.Rat // delegation shares recieved
Height int64 // last height bond updated
}
```
The queue is ordered so the next element to unbond/re-delegate is at the head.
Every tick the head of the queue is checked and if the unbonding period has
passed since `InitTime`, the final settlement of the unbonding is started or
re-delegation is executed, and the element is popped from the queue. Each
`QueueElem` is persisted in the store until it is popped from the queue.
### UnbondingDelegation
- index: delegation address
### QueueElemUnbondDelegation
A UnbondingDelegation object is created every time an unbonding is initiated.
The unbond must be completed with a second transaction provided by the
delegation owner after the unbonding period has passed.
QueueElemUnbondDelegation structure is used in the unbonding queue.
- stored object:
```golang
type QueueElemUnbondDelegation struct {
QueueElem
Payout Address // account to pay out to
Tokens coin.Coins // the value in Atoms of the amount of delegator shares which are unbonding
StartSlashRatio rational.Rat // candidate slash ratio
type UnbondingDelegation struct {
DelegationKey sdk.Address // key of the delegation
ExpectedTokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding
StartSlashRatio sdk.Rat // validator slash ratio at unbonding initiation
CompleteTime int64 // unix time to complete redelegation
CompleteHeight int64 // block height to complete redelegation
}
```
### QueueElemReDelegate
### Redelegation
- index 1: delegation address
- index 2: source validator owner address
- index 3: destination validator owner address
QueueElemReDelegate structure is used in the re-delegation queue.
A redelegation object is created every time a redelegation occurs. The
redelegation must be completed with a second transaction provided by the
delegation owner after the unbonding period has passed. The destination
delegation of a redelegation may not itself undergo a new redelegation until
the original redelegation has been completed.
- stored object:
```golang
type QueueElemReDelegate struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
NewCandidate crypto.PubKey // validator to bond to after unbond
type Redelegation struct {
SourceDelegation sdk.Address // source delegation key
DestinationDelegation sdk.Address // destination delegation key
SourceShares sdk.Rat // amount of source shares redelegating
DestinationShares sdk.Rat // amount of destination shares created at redelegation
SourceStartSlashRatio sdk.Rat // source validator slash ratio at unbonding initiation
CompleteTime int64 // unix time to complete redelegation
CompleteHeight int64 // block height to complete redelegation
}
```

View File

@ -1,67 +1,61 @@
### Transaction Overview
Available Transactions:
* TxDeclareCandidacy
* TxEditCandidacy
* TxDelegate
* TxUnbond
* TxRedelegate
* TxProveLive
In this section we describe the processing of the transactions and the
corresponding updates to the state. Transactions:
- TxCreateValidator
- TxEditValidator
- TxDelegation
- TxStartUnbonding
- TxCompleteUnbonding
- TxRedelegate
- TxCompleteRedelegation
## Transaction processing
Other important state changes:
- Update Validators
In this section we describe the processing of the transactions and the
corresponding updates to the global state. In the following text we will use
`gs` to refer to the `GlobalState` data structure, `unbondDelegationQueue` is a
reference to the queue of unbond delegations, `reDelegationQueue` is the
reference for the queue of redelegations. We use `tx` to denote a
reference to a transaction that is being processed, and `sender` to denote the
address of the sender of the transaction. We use function
`loadCandidate(store, PubKey)` to obtain a Candidate structure from the store,
and `saveCandidate(store, candidate)` to save it. Similarly, we use
`loadDelegatorBond(store, sender, PubKey)` to load a delegator bond with the
key (sender and PubKey) from the store, and
`saveDelegatorBond(store, sender, bond)` to save it.
`removeDelegatorBond(store, sender, bond)` is used to remove the bond from the
store.
Other notes:
- `tx` denotes a reference to the transaction being processed
- `sender` denotes the address of the sender of the transaction
- `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and
modify objects from the store
- `sdk.Rat` refers to a rational numeric type specified by the SDK.
### TxDeclareCandidacy
### TxCreateValidator
A validator candidacy is declared using the `TxDeclareCandidacy` transaction.
A validator is created using the `TxCreateValidator` transaction.
```golang
type TxDeclareCandidacy struct {
type TxCreateValidator struct {
OwnerAddr sdk.Address
ConsensusPubKey crypto.PubKey
Amount coin.Coin
GovernancePubKey crypto.PubKey
Commission rational.Rat
CommissionMax int64
CommissionMaxChange int64
SelfDelegation coin.Coin
Description Description
Commission sdk.Rat
CommissionMax sdk.Rat
CommissionMaxChange sdk.Rat
}
declareCandidacy(tx TxDeclareCandidacy):
candidate = loadCandidate(store, tx.PubKey)
if candidate != nil return // candidate with that public key already exists
createValidator(tx TxCreateValidator):
validator = getValidator(tx.OwnerAddr)
if validator != nil return // only one validator per address
candidate = NewCandidate(tx.PubKey)
candidate.Status = Unbonded
candidate.Owner = sender
init candidate VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero
init commision related fields based on the values from tx
candidate.ProposerRewardPool = Coin(0)
candidate.Description = tx.Description
validator = NewValidator(OwnerAddr, ConsensusPubKey, GovernancePubKey, Description)
init validator poolShares, delegatorShares set to 0
init validator commision fields from tx
validator.PoolShares = 0
saveCandidate(store, candidate)
setValidator(validator)
txDelegate = TxDelegate(tx.PubKey, tx.Amount)
return delegateWithCandidate(txDelegate, candidate)
// see delegateWithCandidate function in [TxDelegate](TxDelegate)
txDelegate = TxDelegate(tx.OwnerAddr, tx.OwnerAddr, tx.SelfDelegation)
delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate)
return
```
### TxEditCandidacy
### TxEditValidator
If either the `Description` (excluding `DateBonded` which is constant),
`Commission`, or the `GovernancePubKey` need to be updated, the
@ -70,214 +64,268 @@ If either the `Description` (excluding `DateBonded` which is constant),
```golang
type TxEditCandidacy struct {
GovernancePubKey crypto.PubKey
Commission int64
Commission sdk.Rat
Description Description
}
editCandidacy(tx TxEditCandidacy):
candidate = loadCandidate(store, tx.PubKey)
if candidate == nil or candidate.Status == Revoked return
validator = getValidator(tx.ValidatorAddr)
if tx.GovernancePubKey != nil candidate.GovernancePubKey = tx.GovernancePubKey
if tx.Commission >= 0 candidate.Commission = tx.Commission
if tx.Description != nil candidate.Description = tx.Description
if tx.Commission > CommissionMax || tx.Commission < 0 then fail
if rateChange(tx.Commission) > CommissionMaxChange then fail
validator.Commission = tx.Commission
if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey
if tx.Description != nil validator.Description = tx.Description
saveCandidate(store, candidate)
setValidator(store, validator)
return
```
### TxDelegate
### TxDelegation
Delegator bonds are created using the `TxDelegate` transaction. Within this
transaction the delegator provides an amount of coins, and in return receives
some amount of candidate's delegator shares that are assigned to
`DelegatorBond.Shares`.
Within this transaction the delegator provides coins, and in return receives
some amount of their validator's delegator-shares that are assigned to
`Delegation.Shares`.
```golang
type TxDelegate struct {
PubKey crypto.PubKey
Amount coin.Coin
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
Amount sdk.Coin
}
delegate(tx TxDelegate):
candidate = loadCandidate(store, tx.PubKey)
if candidate == nil return
return delegateWithCandidate(tx, candidate)
pool = getPool()
if validator.Status == Revoked return
delegateWithCandidate(tx TxDelegate, candidate Candidate):
if candidate.Status == Revoked return
if candidate.Status == Bonded
poolAccount = params.HoldBonded
else
poolAccount = params.HoldUnbonded
delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr)
if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr)
err = transfer(sender, poolAccount, tx.Amount)
if err != nil return
bond = loadDelegatorBond(store, sender, tx.PubKey)
if bond == nil then bond = DelegatorBond(tx.PubKey, rational.Zero, Coin(0), Coin(0))
issuedDelegatorShares = addTokens(tx.Amount, candidate)
bond.Shares += issuedDelegatorShares
saveCandidate(store, candidate)
saveDelegatorBond(store, sender, bond)
saveGlobalState(store, gs)
return
addTokens(amount coin.Coin, candidate Candidate):
if candidate.Status == Bonded
gs.BondedPool += amount
issuedShares = amount / exchangeRate(gs.BondedShares, gs.BondedPool)
gs.BondedShares += issuedShares
else
gs.UnbondedPool += amount
issuedShares = amount / exchangeRate(gs.UnbondedShares, gs.UnbondedPool)
gs.UnbondedShares += issuedShares
candidate.GlobalStakeShares += issuedShares
validator, pool, issuedDelegatorShares = validator.addTokensFromDel(tx.Amount, pool)
delegation.Shares += issuedDelegatorShares
if candidate.IssuedDelegatorShares.IsZero()
exRate = rational.One
else
exRate = candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
issuedDelegatorShares = issuedShares / exRate
candidate.IssuedDelegatorShares += issuedDelegatorShares
return issuedDelegatorShares
exchangeRate(shares rational.Rat, tokenAmount int64):
if shares.IsZero() then return rational.One
return tokenAmount / shares
setDelegation(delegation)
updateValidator(validator)
setPool(pool)
return
```
### TxUnbond
### TxStartUnbonding
Delegator unbonding is defined with the following transaction:
```golang
type TxUnbond struct {
PubKey crypto.PubKey
Shares rational.Rat
type TxStartUnbonding struct {
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
Shares string
}
unbond(tx TxUnbond):
bond = loadDelegatorBond(store, sender, tx.PubKey)
if bond == nil return
if bond.Shares < tx.Shares return
bond.Shares -= tx.Shares
candidate = loadCandidate(store, tx.PubKey)
revokeCandidacy = false
if bond.Shares.IsZero()
if sender == candidate.Owner and candidate.Status != Revoked then revokeCandidacy = true then removeDelegatorBond(store, sender, bond)
else
saveDelegatorBond(store, sender, bond)
if candidate.Status == Bonded
poolAccount = params.HoldBonded
else
poolAccount = params.HoldUnbonded
returnedCoins = removeShares(candidate, shares)
unbondDelegationElem = QueueElemUnbondDelegation(tx.PubKey, currentHeight(), sender, returnedCoins, startSlashRatio)
unbondDelegationQueue.add(unbondDelegationElem)
transfer(poolAccount, unbondingPoolAddress, returnCoins)
startUnbonding(tx TxStartUnbonding):
delegation, found = getDelegatorBond(store, sender, tx.PubKey)
if !found == nil return
if revokeCandidacy
if candidate.Status == Bonded then bondedToUnbondedPool(candidate)
candidate.Status = Revoked
if bond.Shares < tx.Shares
return ErrNotEnoughBondShares
if candidate.IssuedDelegatorShares.IsZero()
removeCandidate(store, tx.PubKey)
else
saveCandidate(store, candidate)
validator, found = GetValidator(tx.ValidatorAddr)
if !found {
return err
saveGlobalState(store, gs)
return
bond.Shares -= tx.Shares
removeShares(candidate Candidate, shares rational.Rat):
globalPoolSharesToRemove = delegatorShareExRate(candidate) * shares
revokeCandidacy = false
if bond.Shares.IsZero() {
if candidate.Status == Bonded
gs.BondedShares -= globalPoolSharesToRemove
removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * globalPoolSharesToRemove
gs.BondedPool -= removedTokens
else
gs.UnbondedShares -= globalPoolSharesToRemove
removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * globalPoolSharesToRemove
gs.UnbondedPool -= removedTokens
candidate.GlobalStakeShares -= removedTokens
candidate.IssuedDelegatorShares -= shares
return returnedCoins
if bond.DelegatorAddr == validator.Owner && validator.Revoked == false
revokeCandidacy = true
delegatorShareExRate(candidate Candidate):
if candidate.IssuedDelegatorShares.IsZero() then return rational.One
return candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
bondedToUnbondedPool(candidate Candidate):
removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * candidate.GlobalStakeShares
gs.BondedShares -= candidate.GlobalStakeShares
gs.BondedPool -= removedTokens
gs.UnbondedPool += removedTokens
issuedShares = removedTokens / exchangeRate(gs.UnbondedShares, gs.UnbondedPool)
gs.UnbondedShares += issuedShares
candidate.GlobalStakeShares = issuedShares
candidate.Status = Unbonded
removeDelegation( bond)
else
bond.Height = currentBlockHeight
setDelegation(bond)
return transfer(address of the bonded pool, address of the unbonded pool, removedTokens)
pool = GetPool()
validator, pool, returnAmount = validator.removeDelShares(pool, tx.Shares)
setPool( pool)
unbondingDelegation = NewUnbondingDelegation(sender, returnAmount, currentHeight/Time, startSlashRatio)
setUnbondingDelegation(unbondingDelegation)
if revokeCandidacy
validator.Revoked = true
validator = updateValidator(validator)
if validator.DelegatorShares == 0 {
removeValidator(validator.Owner)
return
```
### TxRedelegate
### TxCompleteUnbonding
The re-delegation command allows delegators to switch validators while still
receiving equal reward to as if they had never unbonded.
Complete the unbonding and transfer the coins to the delegate. Perform any
slashing that occured during the unbonding period.
```golang
type TxRedelegate struct {
PubKeyFrom crypto.PubKey
PubKeyTo crypto.PubKey
Shares rational.Rat
type TxUnbondingComplete struct {
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
}
redelegate(tx TxRedelegate):
bond = loadDelegatorBond(store, sender, tx.PubKey)
if bond == nil then return
if bond.Shares < tx.Shares return
candidate = loadCandidate(store, tx.PubKeyFrom)
if candidate == nil return
candidate.RedelegatingShares += tx.Shares
reDelegationElem = QueueElemReDelegate(tx.PubKeyFrom, currentHeight(), sender, tx.Shares, tx.PubKeyTo)
redelegationQueue.add(reDelegationElem)
redelegationComplete(tx TxRedelegate):
unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator)
if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight
validator = GetValidator(tx.ValidatorAddr)
returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio
AddCoins(unbonding.DelegatorAddr, returnTokens)
removeUnbondingDelegation(unbonding)
return
```
### TxProveLive
### TxRedelegation
If a validator was automatically unbonded due to liveness issues and wishes to
assert it is still online, it can send `TxProveLive`:
The redelegation command allows delegators to instantly switch validators. Once
the unbonding period has passed, the redelegation must be completed with
txRedelegationComplete.
```golang
type TxProveLive struct {
PubKey crypto.PubKey
type TxRedelegate struct {
DelegatorAddr Address
ValidatorFrom Validator
ValidatorTo Validator
Shares sdk.Rat
CompletedTime int64
}
redelegate(tx TxRedelegate):
pool = getPool()
delegation = getDelegatorBond(tx.DelegatorAddr, tx.ValidatorFrom.Owner)
if delegation == nil
return
if delegation.Shares < tx.Shares
return
delegation.shares -= Tx.Shares
validator, pool, createdCoins = validator.RemoveShares(pool, tx.Shares)
setPool(pool)
redelegation = newRedelegation(tx.DelegatorAddr, tx.validatorFrom,
tx.validatorTo, tx.Shares, createdCoins, tx.CompletedTime)
setRedelegation(redelegation)
return
```
All delegators in the temporary unbonding pool which have not
transacted to move will be bonded back to the now-live validator and begin to
once again collect provisions and rewards.
### TxCompleteRedelegation
Note that unlike TxCompleteUnbonding slashing of redelegating shares does not
take place during completion. Slashing on redelegated shares takes place
actively as a slashing occurs.
```golang
type TxRedelegationComplete struct {
DelegatorAddr Address
ValidatorFrom Validator
ValidatorTo Validator
}
redelegationComplete(tx TxRedelegate):
redelegation = getRedelegation(tx.DelegatorAddr, tx.validatorFrom, tx.validatorTo)
if redelegation.CompleteTime >= CurrentBlockTime && redelegation.CompleteHeight >= CurrentBlockHeight
removeRedelegation(redelegation)
return
```
TODO: pseudo-code
### Update Validators
Within many transactions the validator set must be updated based on changes in
power to a single validator. This process also updates the Tendermint-Updates
store for use in end-block when validators are either added or kicked from the
Tendermint.
```golang
updateBondedValidators(newValidator Validator) (updatedVal Validator)
kickCliffValidator = false
oldCliffValidatorAddr = getCliffValidator(ctx)
// add the actual validator power sorted store
maxValidators = GetParams(ctx).MaxValidators
iterator = ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest
bondedValidatorsCount = 0
var validator Validator
for {
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
setCliffValidator(ctx, validator, GetPool(ctx))
iterator.Close()
break
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
ownerAddr = iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator
else
validator = getValidator(ownerAddr)
// if not previously a validator (and unrevoked),
// kick the cliff validator / bond this new validator
if validator.Status() != Bonded && !validator.Revoked {
kickCliffValidator = true
validator = bondValidator(ctx, store, validator)
if bytes.Equal(ownerAddr, newValidator.Owner) {
updatedVal = validator
bondedValidatorsCount++
iterator.Next()
// perform the actual kicks
if oldCliffValidatorAddr != nil && kickCliffValidator {
validator = getValidator(store, oldCliffValidatorAddr)
unbondValidator(ctx, store, validator)
return
// perform all the store operations for when a validator status becomes unbonded
unbondValidator(ctx Context, store KVStore, validator Validator)
pool = GetPool(ctx)
// set the status
validator, pool = validator.UpdateStatus(pool, Unbonded)
setPool(ctx, pool)
// save the now unbonded validator record
setValidator(validator)
// add to accumulated changes for tendermint
setTendermintUpdates(validator.abciValidatorZero)
// also remove from the bonded validators index
removeValidatorsBonded(validator)
}
// perform all the store operations for when a validator status becomes bonded
bondValidator(ctx Context, store KVStore, validator Validator) Validator
pool = GetPool(ctx)
// set the status
validator, pool = validator.UpdateStatus(pool, Bonded)
setPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
setValidator(validator)
setValidatorsBonded(validator)
// add to accumulated changes for tendermint
setTendermintUpdates(validator.abciValidator)
return validator
```

View File

@ -1,190 +0,0 @@
# Validator Set Changes
The validator set may be updated by state transitions that run at the beginning and
end of every block. This can happen one of three ways:
- voting power of a validator changes due to bonding and unbonding
- voting power of validator is "slashed" due to conflicting signed messages
- validator is automatically unbonded due to inactivity
## Voting Power Changes
At the end of every block, we run the following:
(TODO remove inflation from here)
```golang
tick(ctx Context):
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
time = ctx.Time()
if time > gs.InflationLastTime + ProvisionTimeout
gs.InflationLastTime = time
gs.Inflation = nextInflation(hrsPerYr).Round(1000000000)
provisions = gs.Inflation * (gs.TotalSupply / hrsPerYr)
gs.BondedPool += provisions
gs.TotalSupply += provisions
saveGlobalState(store, gs)
if time > unbondDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
transfer(unbondingQueueAddress, elem.Payout, elem.Tokens)
unbondDelegationQueue.remove(elem)
if time > reDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
candidate = getCandidate(store, elem.PubKey)
returnedCoins = removeShares(candidate, elem.Shares)
candidate.RedelegatingShares -= elem.Shares
delegateWithCandidate(TxDelegate(elem.NewCandidate, returnedCoins), candidate)
reDelegationQueue.remove(elem)
return UpdateValidatorSet()
nextInflation(hrsPerYr rational.Rat):
if gs.TotalSupply > 0
bondedRatio = gs.BondedPool / gs.TotalSupply
else
bondedRation = 0
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear / hrsPerYr
inflation = gs.Inflation + inflationRateChange
if inflation > params.InflationMax then inflation = params.InflationMax
if inflation < params.InflationMin then inflation = params.InflationMin
return inflation
UpdateValidatorSet():
candidates = loadCandidates(store)
v1 = candidates.Validators()
v2 = updateVotingPower(candidates).Validators()
change = v1.validatorsUpdated(v2) // determine all updated validators between two validator sets
return change
updateVotingPower(candidates Candidates):
foreach candidate in candidates do
candidate.VotingPower = (candidate.IssuedDelegatorShares - candidate.RedelegatingShares) * delegatorShareExRate(candidate)
candidates.Sort()
foreach candidate in candidates do
if candidate is not in the first params.MaxVals
candidate.VotingPower = rational.Zero
if candidate.Status == Bonded then bondedToUnbondedPool(candidate Candidate)
else if candidate.Status == UnBonded then unbondedToBondedPool(candidate)
saveCandidate(store, c)
return candidates
unbondedToBondedPool(candidate Candidate):
removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * candidate.GlobalStakeShares
gs.UnbondedShares -= candidate.GlobalStakeShares
gs.UnbondedPool -= removedTokens
gs.BondedPool += removedTokens
issuedShares = removedTokens / exchangeRate(gs.BondedShares, gs.BondedPool)
gs.BondedShares += issuedShares
candidate.GlobalStakeShares = issuedShares
candidate.Status = Bonded
return transfer(address of the unbonded pool, address of the bonded pool, removedTokens)
```
## Slashing
Messges which may compromise the safety of the underlying consensus protocol ("equivocations")
result in some amount of the offending validator's shares being removed ("slashed").
Currently, such messages include only the following:
- prevotes by the same validator for more than one BlockID at the same
Height and Round
- precommits by the same validator for more than one BlockID at the same
Height and Round
We call any such pair of conflicting votes `Evidence`. Full nodes in the network prioritize the
detection and gossipping of `Evidence` so that it may be rapidly included in blocks and the offending
validators punished.
For some `evidence` to be valid, it must satisfy:
`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE`
where `evidence.Timestamp` is the timestamp in the block at height
`evidence.Height` and `block.Timestamp` is the current block timestamp.
If valid evidence is included in a block, the offending validator loses
a constant `SLASH_PROPORTION` of their current stake at the beginning of the block:
```
oldShares = validator.shares
validator.shares = oldShares * (1 - SLASH_PROPORTION)
```
This ensures that offending validators are punished the same amount whether they
act as a single validator with X stake or as N validators with collectively X
stake.
## Automatic Unbonding
Every block includes a set of precommits by the validators for the previous block,
known as the LastCommit. A LastCommit is valid so long as it contains precommits from +2/3 of voting power.
Proposers are incentivized to include precommits from all
validators in the LastCommit by receiving additional fees
proportional to the difference between the voting power included in the
LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)).
Validators are penalized for failing to be included in the LastCommit for some
number of blocks by being automatically unbonded.
The following information is stored with each validator candidate, and is only non-zero if the candidate becomes an active validator:
```go
type ValidatorSigningInfo struct {
StartHeight int64
SignedBlocksBitArray BitArray
}
```
Where:
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
* `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks,
whether or not this validator was included in the LastCommit. It uses a `0` if the validator was included, and a `1` if it was not.
Note it is initialized with all 0s.
At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded:
```
h = block.Height
index = h % SIGNED_BLOCKS_WINDOW
for val in block.Validators:
signInfo = val.SignInfo
if val in block.LastCommit:
signInfo.SignedBlocksBitArray.Set(index, 0)
else
signInfo.SignedBlocksBitArray.Set(index, 1)
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
blocksSigned = signInfo.SignedBlocksBitArray.Sum()
if h > minHeight AND blocksSigned < minSigned:
unbond the validator
```

View File

@ -50,7 +50,7 @@ func main() {
}
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", baseApp)
srv, err := server.NewServer("0.0.0.0:26658", "socket", baseApp)
if err != nil {
fmt.Println(err)
os.Exit(1)

View File

@ -9,7 +9,7 @@
- name: Gather status
uri:
body_format: json
url: "http://{{inventory_hostname}}:46657/status"
url: "http://{{inventory_hostname}}:26657/status"
register: status
- name: Print status

View File

@ -0,0 +1,51 @@
#!/usr/bin/tcsh
# Just run tcsh install_sdk_bsd.sh
# XXX: this script is intended to be run from
# a fresh Digital Ocean droplet with FreeBSD
# upon its completion, you must either reset
# your terminal or run `source ~/.tcshrc`
# This assumes your installing it through tcsh as root.
# Change the relevant lines from tcsh to csh if your
# installing as a different user, along with changing the
# gopath.
# change this to a specific release or branch
set BRANCH=master
sudo pkg update
sudo pkg upgrade -y
sudo pkg install -y gmake
sudo pkg install -y git
# get and unpack golang
curl -O https://storage.googleapis.com/golang/go1.10.freebsd-amd64.tar.gz
tar -xvf go1.10.freebsd-amd64.tar.gz
# move go binary and add to path
mv go /usr/local
set path=($path /usr/local/go/bin)
# create the go directory, set GOPATH, and put it on PATH
mkdir go
echo "setenv GOPATH /root/go" >> ~/.tcshrc
setenv GOPATH /root/go
echo "set path=($path $GOPATH/bin)" >> ~/.tcshrc
source ~/.tcshrc
# get the code and move into repo
set REPO=github.com/cosmos/cosmos-sdk
go get $REPO
cd $GOPATH/src/$REPO
# build & install master
git checkout $BRANCH
gmake get_tools
gmake get_vendor_deps
gmake install
gmake install_examples

View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# XXX: this script is intended to be run from
# a fresh Digital Ocean droplet with Ubuntu
# upon its completion, you must either reset
# your terminal or run `source ~/.profile`
# change this to a specific release or branch
BRANCH=master
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y make
# get and unpack golang
curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz
tar -xvf go1.10.linux-amd64.tar.gz
# move go binary and add to path
mv go /usr/local
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile
# create the goApps directory, set GOPATH, and put it on PATH
mkdir goApps
echo "export GOPATH=/root/goApps" >> ~/.profile
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile
source ~/.profile
# get the code and move into repo
REPO=github.com/cosmos/cosmos-sdk
go get $REPO
cd $GOPATH/src/$REPO
# build & install master
git checkout $BRANCH
make get_tools
make get_vendor_deps
make install
make install_examples

View File

@ -310,7 +310,7 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) (
if len(persistentPeers) == 0 {
comma = ""
}
persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTx.NodeID, genTx.IP)
persistentPeers += fmt.Sprintf("%s%s@%s:26656", comma, genTx.NodeID, genTx.IP)
}
return

View File

@ -37,7 +37,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
// basic flags for abci app
cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint")
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:46658", "Listen address")
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
// AddNodeFlags adds support for all tendermint-specific command line options
tcmd.AddNodeFlags(cmd)

View File

@ -72,7 +72,7 @@ func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
Short: "Reset blockchain database, priv_validator.json file, and the logger",
RunE: func(cmd *cobra.Command, args []string) error {
cfg := ctx.Config
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), ctx.Logger)
tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger)
return nil
},
}

View File

@ -7,16 +7,65 @@ import (
"time"
amino "github.com/tendermint/go-amino"
tmclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
)
// Uses localhost
func WaitForHeight(height int64, port string) {
// Wait for the next tendermint block from the Tendermint RPC
// on localhost
func WaitForNextHeightTM(port string) {
url := fmt.Sprintf("http://localhost:%v", port)
cl := tmclient.NewHTTP(url, "/websocket")
resBlock, err := cl.Block(nil)
if err != nil {
panic(err)
}
waitForHeightTM(resBlock.Block.Height+1, url)
}
// Wait for the given height from the Tendermint RPC
// on localhost
func WaitForHeightTM(height int64, port string) {
url := fmt.Sprintf("http://localhost:%v", port)
waitForHeightTM(height, url)
}
func waitForHeightTM(height int64, url string) {
cl := tmclient.NewHTTP(url, "/websocket")
for {
// get url, try a few times
var resBlock *ctypes.ResultBlock
var err error
INNER:
for i := 0; i < 5; i++ {
resBlock, err = cl.Block(nil)
if err == nil {
break INNER
}
time.Sleep(time.Millisecond * 200)
}
if err != nil {
panic(err)
}
url := fmt.Sprintf("http://localhost:%v/blocks/latest", port)
if resBlock.Block != nil &&
resBlock.Block.Height >= height {
fmt.Println("HEIGHT", resBlock.Block.Height)
return
}
time.Sleep(time.Millisecond * 100)
}
}
// Wait for height from the LCD API on localhost
func WaitForHeight(height int64, port string) {
url := fmt.Sprintf("http://localhost:%v/blocks/latest", port)
waitForHeight(height, url)
}
func waitForHeight(height int64, url string) {
for {
// get url, try a few times
var res *http.Response
var err error
@ -25,7 +74,7 @@ func WaitForHeight(height int64, port string) {
if err == nil {
break
}
time.Sleep(time.Second)
time.Sleep(time.Millisecond * 200)
}
if err != nil {
panic(err)
@ -45,7 +94,8 @@ func WaitForHeight(height int64, port string) {
panic(err)
}
if resultBlock.Block.Height >= height {
if resultBlock.Block != nil &&
resultBlock.Block.Height >= height {
return
}
time.Sleep(time.Millisecond * 100)

View File

@ -2,7 +2,6 @@ package version
import (
"fmt"
"net/http"
"github.com/spf13/cobra"
)
@ -16,7 +15,8 @@ var (
}
)
func getVersion() string {
// return version of CLI/node and commit hash
func GetVersion() string {
v := Version
if GitCommit != "" {
v = v + "-" + GitCommit
@ -26,12 +26,6 @@ func getVersion() string {
// CMD
func printVersion(cmd *cobra.Command, args []string) {
v := getVersion()
v := GetVersion()
fmt.Println(v)
}
// version REST handler endpoint
func RequestHandler(w http.ResponseWriter, r *http.Request) {
v := getVersion()
w.Write([]byte(v))
}

View File

@ -47,7 +47,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
// perform query
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(auth.AddressStoreKey(key), storeName)
res, err := ctx.QueryStore(auth.AddressStoreKey(key), storeName)
if err != nil {
return err
}

View File

@ -34,7 +34,7 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder aut
return
}
res, err := ctx.Query(auth.AddressStoreKey(addr), storeName)
res, err := ctx.QueryStore(auth.AddressStoreKey(addr), storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query account. Error: %s", err.Error())))

View File

@ -58,7 +58,7 @@ I[04-02|14:09:14.453] Generated genesis file module=main p
}
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
> ID2=test-chain-4XHTPn
> NODE2=tcp://0.0.0.0:46657
> NODE2=tcp://0.0.0.0:26657
> basecli keys add key2 --recover
Enter a passphrase for your key:
Repeat the passphrase:
@ -70,7 +70,7 @@ key2 DC26002735D3AA9573707CFA6D77C12349E49868
> basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656
...
> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:46658 --rpc.laddr tcp://0.0.0.0:46657 --p2p.laddr tcp://0.0.0.0:46656
> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:26658 --rpc.laddr tcp://0.0.0.0:26657 --p2p.laddr tcp://0.0.0.0:26656
...
```
## Check balance

View File

@ -54,7 +54,7 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
}
cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets")
cmd.Flags().String(FlagFromChainNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
cmd.Flags().String(FlagFromChainNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets")
cmd.Flags().String(FlagToChainNode, "tcp://localhost:36657", "<host>:<port> to tendermint rpc interface for this chain")
@ -151,7 +151,7 @@ OUTER:
}
func query(node string, key []byte, storeName string) (res []byte, err error) {
return context.NewCoreContextFromViper().WithNodeURI(node).Query(key, storeName)
return context.NewCoreContextFromViper().WithNodeURI(node).QueryStore(key, storeName)
}
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {

View File

@ -27,7 +27,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command {
}
key := slashing.GetValidatorSigningInfoKey(pk.Address())
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}

View File

@ -27,7 +27,7 @@ func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command {
}
key := stake.GetValidatorKey(addr)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}
@ -124,7 +124,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
key := stake.GetDelegationKey(delAddr, addr, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
return err
}

View File

@ -48,7 +48,7 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
res, err := ctx.Query(key, storeName)
res, err := ctx.QueryStore(key, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))

View File

@ -10,7 +10,6 @@ import (
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// pubKey.
// TODO better way of managing space
type Delegation struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"validator_addr"`
@ -50,5 +49,4 @@ func (b Delegation) HumanReadableString() (string, error) {
resp += fmt.Sprintf("Height: %d", b.Height)
return resp, nil
}

View File

@ -294,16 +294,14 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator
return validator
}
// XXX TODO build in consideration for revoked
//
// Update the validator group and kick out any old validators. In addition this
// function adds (or doesn't add) a validator which has updated its bonded
// tokens to the validator group. -> this validator is specified through the
// updatedValidatorAddr term.
//
// The correct subset is retrieved by iterating through an index of the
// validators sorted by power, stored using the ValidatorsByPowerKey. Simultaniously
// the current validator records are updated in store with the
// validators sorted by power, stored using the ValidatorsByPowerKey.
// Simultaneously the current validator records are updated in store with the
// ValidatorsBondedKey. This store is used to determine if a validator is a
// validator without needing to iterate over the subspace as we do in
// GetValidators.
@ -331,10 +329,10 @@ func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore,
break
}
// either retrieve the original validator from the store,
// or under the situation that this is the "new validator" just
// use the validator provided because it has not yet been updated
// in the main validator store
// either retrieve the original validator from the store, or under the
// situation that this is the "new validator" just use the validator
// provided because it has not yet been updated in the main validator
// store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, newValidator.Owner) {
validator = newValidator

View File

@ -4,9 +4,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// kind of shares
type PoolShareKind byte
// pool shares held by a validator
type PoolShares struct {
Status sdk.BondStatus `json:"status"`

View File

@ -82,10 +82,10 @@ func (v Validator) equal(c2 Validator) bool {
// Description - description fields for a validator
type Description struct {
Moniker string `json:"moniker"`
Identity string `json:"identity"`
Website string `json:"website"`
Details string `json:"details"`
Moniker string `json:"moniker"` // name
Identity string `json:"identity"` // optional identity signature (ex. UPort or Keybase)
Website string `json:"website"` // optional website link
Details string `json:"details"` // optional details
}
func NewDescription(moniker, identity, website, details string) Description {