Merge branch 'develop' into greg/testnet-command-2
This commit is contained in:
commit
f8290a0fc3
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
*TBD*
|
*TBD*
|
||||||
|
|
||||||
|
BREAKING CHANGES
|
||||||
|
* Change default ports from 466xx to 266xx
|
||||||
|
|
||||||
## 0.19.0
|
## 0.19.0
|
||||||
|
|
||||||
*June 13, 2018*
|
*June 13, 2018*
|
||||||
|
@ -14,6 +17,7 @@ BREAKING CHANGES
|
||||||
|
|
||||||
FEATURES
|
FEATURES
|
||||||
* [x/auth] Added AccountNumbers to BaseAccount and StdTxs to allow for replay protection with account pruning
|
* [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
|
IMPROVEMENTS
|
||||||
* export command now writes current validator set for Tendermint
|
* export command now writes current validator set for Tendermint
|
||||||
|
@ -26,14 +30,18 @@ IMPROVEMENTS
|
||||||
|
|
||||||
FIXES
|
FIXES
|
||||||
* Fixes consensus fault on testnet - see postmortem [here](https://github.com/cosmos/cosmos-sdk/issues/1197#issuecomment-396823021)
|
* 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] Switch to bech32 for addresses on all human readable inputs and outputs
|
||||||
* [lcd] fixed tx indexing/querying
|
* [lcd] fixed tx indexing/querying
|
||||||
* [cli] Added `--gas` flag to specify transaction gas limit
|
* [cli] Added `--gas` flag to specify transaction gas limit
|
||||||
* [gaia] Registered slashing message handler
|
* [gaia] Registered slashing message handler
|
||||||
* [x/slashing] Set signInfo.StartHeight correctly for newly bonded validators
|
* [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*
|
*June 9, 2018*
|
||||||
|
|
||||||
|
|
|
@ -267,8 +267,8 @@
|
||||||
"server",
|
"server",
|
||||||
"types"
|
"types"
|
||||||
]
|
]
|
||||||
revision = "ebee2fe114020aa49c70bbbae50b7079fc7e7b90"
|
revision = "198dccf0ddfd1bb176f87657e3286a05a6ed9540"
|
||||||
version = "v0.11.0"
|
version = "v0.12.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -347,8 +347,7 @@
|
||||||
"types",
|
"types",
|
||||||
"version"
|
"version"
|
||||||
]
|
]
|
||||||
revision = "27bd1deabe4ba6a2d9b463b8f3e3f1e31b993e61"
|
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
|
||||||
version = "v0.20.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "develop"
|
branch = "develop"
|
||||||
|
@ -463,6 +462,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "31f69b235b2d8f879a215c9e8ca0919adc62d21f6830b17931a3a0efb058721f"
|
inputs-digest = "d02a24bcfd8bded901e1b154e19b81ff797d3921046ede19d1d11eed61e871e7"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/abci"
|
name = "github.com/tendermint/abci"
|
||||||
version = "=0.11.0"
|
version = "=0.12.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/go-crypto"
|
name = "github.com/tendermint/go-crypto"
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/tendermint"
|
name = "github.com/tendermint/tendermint"
|
||||||
version = "=0.20.0"
|
revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601"
|
||||||
|
|
||||||
[[override]]
|
[[override]]
|
||||||
name = "github.com/tendermint/tmlibs"
|
name = "github.com/tendermint/tmlibs"
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/version"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
)
|
)
|
||||||
|
@ -338,6 +339,11 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||||
} else {
|
} else {
|
||||||
result = app.Simulate(tx)
|
result = app.Simulate(tx)
|
||||||
}
|
}
|
||||||
|
case "version":
|
||||||
|
return abci.ResponseQuery{
|
||||||
|
Code: uint32(sdk.ABCICodeOK),
|
||||||
|
Value: []byte(version.GetVersion()),
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
|
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
func RunForever(app abci.Application) {
|
func RunForever(app abci.Application) {
|
||||||
|
|
||||||
// Start the ABCI server
|
// 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 {
|
if err != nil {
|
||||||
cmn.Exit(err.Error())
|
cmn.Exit(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package context
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
@ -42,14 +44,19 @@ func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit,
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query from Tendermint with the provided key and storename
|
// Query information about the connected node
|
||||||
func (ctx CoreContext) Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
|
func (ctx CoreContext) Query(path string) (res []byte, err error) {
|
||||||
return ctx.query(key, storeName, "key")
|
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
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return res, err
|
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
|
// Query from Tendermint with the provided storename and path
|
||||||
func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
|
func (ctx CoreContext) query(path string, key common.HexBytes) (res []byte, err error) {
|
||||||
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
|
|
||||||
node, err := ctx.GetNode()
|
node, err := ctx.GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -80,6 +86,12 @@ func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res [
|
||||||
return resp.Value, nil
|
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
|
// Get the from address from the name flag
|
||||||
func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) {
|
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")
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -201,7 +213,7 @@ func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
|
||||||
return 0, errors.New("accountDecoder required but not provided")
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
// TODO: make this default false when we support proofs
|
// TODO: make this default false when we support proofs
|
||||||
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses")
|
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses")
|
||||||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
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")
|
c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block")
|
||||||
}
|
}
|
||||||
return cmds
|
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().Int64(FlagSequence, 0, "Sequence number to sign the tx")
|
||||||
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
|
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
|
||||||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
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")
|
c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction")
|
||||||
}
|
}
|
||||||
return cmds
|
return cmds
|
||||||
|
|
|
@ -116,6 +116,15 @@ func TestVersion(t *testing.T) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
match := reg.MatchString(body)
|
match := reg.MatchString(body)
|
||||||
assert.True(t, match, 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) {
|
func TestNodeStatus(t *testing.T) {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||||
tx "github.com/cosmos/cosmos-sdk/client/tx"
|
tx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
version "github.com/cosmos/cosmos-sdk/version"
|
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||||
bank "github.com/cosmos/cosmos-sdk/x/bank/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().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().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.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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandler(cdc *wire.Codec) http.Handler {
|
func createHandler(cdc *wire.Codec) http.Handler {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
|
|
||||||
|
|
||||||
kb, err := keys.GetKeyBase() //XXX
|
kb, err := keys.GetKeyBase() //XXX
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,6 +71,8 @@ func createHandler(cdc *wire.Codec) http.Handler {
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// TODO make more functional? aka r = keys.RegisterRoutes(r)
|
// 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)
|
keys.RegisterRoutes(r)
|
||||||
rpc.RegisterRoutes(ctx, r)
|
rpc.RegisterRoutes(ctx, r)
|
||||||
tx.RegisterRoutes(ctx, r, cdc)
|
tx.RegisterRoutes(ctx, r, cdc)
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ func BlockCommand() *cobra.Command {
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
RunE: printBlock,
|
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
|
// TODO: change this to false when we can
|
||||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
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)")
|
cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)")
|
||||||
|
|
|
@ -36,7 +36,7 @@ func initClientCommand() *cobra.Command {
|
||||||
RunE: todoNotImplemented,
|
RunE: todoNotImplemented,
|
||||||
}
|
}
|
||||||
cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to")
|
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(flagGenesis, "", "Genesis file to verify header validity")
|
||||||
cmd.Flags().String(flagCommit, "", "File with trusted and signed header")
|
cmd.Flags().String(flagCommit, "", "File with trusted and signed header")
|
||||||
cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)")
|
cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)")
|
||||||
|
|
|
@ -18,7 +18,7 @@ func statusCommand() *cobra.Command {
|
||||||
Short: "Query remote node for status",
|
Short: "Query remote node for status",
|
||||||
RunE: printNodeStatus,
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func ValidatorCommand() *cobra.Command {
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
RunE: printValidators,
|
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
|
// TODO: change this to false when we can
|
||||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||||
return cmd
|
return cmd
|
||||||
|
|
|
@ -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
|
// TODO: change this to false when we can
|
||||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||||
|
|
|
@ -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
|
// TODO: change this to false once proofs built in
|
||||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -50,7 +49,7 @@ func TestGaiaCLISend(t *testing.T) {
|
||||||
assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak"))
|
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)
|
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))
|
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||||
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
|
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
|
||||||
|
@ -59,7 +58,7 @@ func TestGaiaCLISend(t *testing.T) {
|
||||||
|
|
||||||
// test autosequencing
|
// test autosequencing
|
||||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
|
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))
|
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||||
assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak"))
|
assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak"))
|
||||||
|
@ -96,7 +95,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
|
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))
|
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||||
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
|
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")
|
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
|
||||||
|
|
||||||
executeWrite(t, cvStr, pass)
|
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))
|
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||||
require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
|
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))
|
t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr))
|
||||||
|
|
||||||
executeWrite(t, unbondStr, pass)
|
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))
|
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||||
require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
|
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)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
proc.Wait()
|
proc.Wait()
|
||||||
|
// bz := proc.StdoutBuffer.Bytes()
|
||||||
|
// fmt.Println("EXEC WRITE", string(bz))
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeInit(t *testing.T, cmdStr string) (chainID string) {
|
func executeInit(t *testing.T, cmdStr string) (chainID string) {
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
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
|
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
|
mkdir -p $GOPATH/src/github.com/cosmos
|
||||||
cd $GOPATH/src/github.com/cosmos
|
cd $GOPATH/src/github.com/cosmos
|
||||||
git clone https://github.com/cosmos/cosmos-sdk
|
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
|
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
|
gaiad version
|
||||||
0.18.0-eceb56b7
|
0.19.0-<commit>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Node Setup
|
### Node Setup
|
||||||
|
@ -76,7 +76,7 @@ Now it is time to upgrade the software:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
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
|
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
|
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
|
### 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
|
# 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).
|
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).
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
# TESTNET STATUS
|
# 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
|
## *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
|
- 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
|
@ -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
|
If you provide the genesis file to basecli, it can calculate the proper
|
||||||
chainID and validator hash. Basecli needs to get this information from
|
chainID and validator hash. Basecli needs to get this information from
|
|
@ -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}')
|
YOU=$(countercli keys get friend | awk '{print $2}')
|
||||||
countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
|
countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
|
|
@ -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"
|
moniker = "anonymous"
|
||||||
fast_sync = true
|
fast_sync = true
|
||||||
db_backend = "leveldb"
|
db_backend = "leveldb"
|
||||||
log_level = "state:info,*:error"
|
log_level = "state:info,*:error"
|
||||||
|
|
||||||
[rpc]
|
[rpc]
|
||||||
laddr = "tcp://0.0.0.0:46667"
|
laddr = "tcp://0.0.0.0:26667"
|
||||||
|
|
||||||
[p2p]
|
[p2p]
|
||||||
laddr = "tcp://0.0.0.0:46666"
|
laddr = "tcp://0.0.0.0:26666"
|
||||||
seeds = "0.0.0.0:46656"
|
seeds = "0.0.0.0:26656"
|
||||||
|
|
||||||
Start Nodes
|
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
|
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
|
||||||
|
|
||||||
To see what tendermint considers the validator set is, use:
|
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.
|
and compare the information in this file: ``~/.gaia1/priv_validator.json``. The ``address`` and ``pub_key`` fields should match.
|
||||||
|
|
|
@ -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:
|
and check our balance:
|
||||||
|
|
14
docs/conf.py
14
docs/conf.py
|
@ -38,9 +38,15 @@ templates_path = ['_templates']
|
||||||
|
|
||||||
# The suffix(es) of source filenames.
|
# The suffix(es) of source filenames.
|
||||||
# You can specify multiple suffix as a list of string:
|
# You can specify multiple suffix as a list of string:
|
||||||
#
|
|
||||||
# source_suffix = ['.rst', '.md']
|
from recommonmark.parser import CommonMarkParser
|
||||||
source_suffix = '.rst'
|
|
||||||
|
source_parsers = {
|
||||||
|
'.md': CommonMarkParser,
|
||||||
|
}
|
||||||
|
|
||||||
|
source_suffix = ['.rst', '.md']
|
||||||
|
#source_suffix = '.rst'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
@ -69,7 +75,7 @@ language = None
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This patterns also effect to html_static_path and html_extra_path
|
# 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.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
|
@ -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)
|
|
@ -17,6 +17,13 @@ paths:
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Plaintext version i.e. "v0.5.0"
|
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:
|
/node_info:
|
||||||
get:
|
get:
|
||||||
description: Only the node info. Block information can be queried via /block/latest
|
description: Only the node info. Block information can be queried via /block/latest
|
||||||
|
@ -41,7 +48,7 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
listen_addr:
|
listen_addr:
|
||||||
type: string
|
type: string
|
||||||
example: 192.168.56.1:46656
|
example: 192.168.56.1:26656
|
||||||
version:
|
version:
|
||||||
description: Tendermint version
|
description: Tendermint version
|
||||||
type: string
|
type: string
|
|
@ -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
|
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
|
there aren't enough validators online. Turn it back on and they will
|
|
@ -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`
|
||||||
|
|
|
@ -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
|
|
@ -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.
|
Finally, to relinquish all your power, unbond some coins. You should see your VotingPower reduce and your account balance increase.
|
||||||
|
|
|
@ -17,8 +17,9 @@ SDK
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
sdk/install.rst
|
guides/sdk/install.md
|
||||||
sdk/key-management.rst
|
guides/sdk/key-management.md
|
||||||
|
|
||||||
.. sdk/overview.rst # needs to be updated
|
.. sdk/overview.rst # needs to be updated
|
||||||
.. old/glossary.rst # not completely up to date but has good content
|
.. old/glossary.rst # not completely up to date but has good content
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ Staking
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
staking/testnet.rst
|
guides/staking/testnet.md
|
||||||
.. staking/intro.rst
|
.. staking/intro.rst
|
||||||
.. staking/key-management.rst
|
.. staking/key-management.rst
|
||||||
.. staking/local-testnet.rst
|
.. staking/local-testnet.rst
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
|
|
@ -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
|
- [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for
|
||||||
sending tokens.
|
sending tokens.
|
||||||
- [Staking](staking) - Proof of Stake related specifications including bonding
|
|
||||||
and delegation transactions, inflation, fees, etc.
|
|
||||||
- [Governance](governance) - Governance related specifications including
|
- [Governance](governance) - Governance related specifications including
|
||||||
proposals and voting.
|
proposals and voting.
|
||||||
- [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol.
|
- [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
|
- [Other](other) - Other components of the Cosmos Hub, including the reserve
|
||||||
pool, All in Bits vesting, etc.
|
pool, All in Bits vesting, etc.
|
||||||
|
|
||||||
|
|
|
@ -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 can’t 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
|
||||||
|
```
|
|
@ -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`
|
|
@ -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
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -2,30 +2,38 @@
|
||||||
|
|
||||||
## Abstract
|
## 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
|
## 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)**
|
1. **[Design overview](overview.md)**
|
||||||
2. **Implementation**
|
2. **Implementation**
|
||||||
1. **[State](state.md)**
|
1. **[State](state.md)**
|
||||||
1. Global State
|
1. Params
|
||||||
2. Validator Candidates
|
1. Pool
|
||||||
3. Delegator Bonds
|
2. Validators
|
||||||
4. Unbond and Rebond Queue
|
3. Delegations
|
||||||
2. **[Transactions](transactions.md)**
|
2. **[Transactions](transactions.md)**
|
||||||
1. Declare Candidacy
|
1. Create-Validator
|
||||||
2. Edit Candidacy
|
2. Edit-Validator
|
||||||
3. Delegate
|
3. Repeal-Revocation
|
||||||
4. Unbond
|
4. Delegate
|
||||||
5. Redelegate
|
5. Unbond
|
||||||
6. ProveLive
|
6. Redelegate
|
||||||
3. **[Validator Set Changes](valset-changes.md)**
|
3. **[Validator Set Changes](valset-changes.md)**
|
||||||
1. Validator set updates
|
1. Validator set updates
|
||||||
2. Slashing
|
2. Slashing
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
@ -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
|
|
||||||
```
|
|
|
@ -1,204 +1,160 @@
|
||||||
|
|
||||||
## State
|
## State
|
||||||
|
|
||||||
The staking module persists the following information to the store:
|
### Pool
|
||||||
* `GlobalState`, a struct describing the global pools, inflation, and
|
- index: n/a single-record
|
||||||
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
|
|
||||||
|
|
||||||
### 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
|
- stored object:
|
||||||
global bonded/unbonded position, the Atom inflation rate, and the fees.
|
|
||||||
|
|
||||||
`Params` is global data structure that stores system parameters and defines overall functioning of the
|
```golang
|
||||||
module.
|
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
|
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Params struct {
|
type PoolShares struct {
|
||||||
HoldBonded Address // account where all bonded coins are held
|
Status sdk.BondStatus // either: unbonded, unbonding, or bonded
|
||||||
HoldUnbonding Address // account where all delegated but unbonding coins are held
|
Amount sdk.Rat // total shares of type ShareKind
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Candidate
|
### Params
|
||||||
|
- index: n/a single-record
|
||||||
|
|
||||||
The `Candidate` holds the current state and some historical
|
Params is global data structure that stores system parameters and defines
|
||||||
actions of validators or candidate-validators.
|
overall functioning of the stake module.
|
||||||
|
|
||||||
``` go
|
- stored object:
|
||||||
type CandidateStatus byte
|
|
||||||
|
|
||||||
const (
|
```golang
|
||||||
Bonded CandidateStatus = 0x01
|
type Params struct {
|
||||||
Unbonded CandidateStatus = 0x02
|
InflationRateChange sdk.Rat // maximum annual change in inflation rate
|
||||||
Revoked CandidateStatus = 0x03
|
InflationMax sdk.Rat // maximum inflation rate
|
||||||
)
|
InflationMin sdk.Rat // minimum inflation rate
|
||||||
|
GoalBonded sdk.Rat // Goal of percent bonded atoms
|
||||||
|
|
||||||
type Candidate struct {
|
MaxValidators uint16 // maximum number of validators
|
||||||
Status CandidateStatus
|
BondDenom string // bondable coin denomination
|
||||||
ConsensusPubKey crypto.PubKey
|
}
|
||||||
GovernancePubKey crypto.PubKey
|
```
|
||||||
Owner crypto.Address
|
|
||||||
GlobalStakeShares rational.Rat
|
### Validator
|
||||||
IssuedDelegatorShares rational.Rat
|
- index 1: validator owner address
|
||||||
RedelegatingShares rational.Rat
|
- index 2: validator Tendermint PubKey
|
||||||
VotingPower rational.Rat
|
- index 3: bonded validators only
|
||||||
Commission rational.Rat
|
- index 4: voting power
|
||||||
CommissionMax rational.Rat
|
|
||||||
CommissionChangeRate rational.Rat
|
Related Store which holds Validator.ABCIValidator()
|
||||||
CommissionChangeToday rational.Rat
|
- index: validator owner address
|
||||||
ProposerRewardPool coin.Coins
|
|
||||||
Adjustment rational.Rat
|
The `Validator` holds the current state and some historical actions of the
|
||||||
Description Description
|
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 {
|
type Description struct {
|
||||||
Name string
|
Moniker string // name
|
||||||
DateBonded string
|
Identity string // optional identity signature (ex. UPort or Keybase)
|
||||||
Identity string
|
Website string // optional website link
|
||||||
Website string
|
Details string // optional details
|
||||||
Details string
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Candidate parameters are described:
|
### Delegation
|
||||||
* Status: it can be Bonded (active validator), Unbonding (validator candidate)
|
- index: delegation address
|
||||||
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
|
|
||||||
|
|
||||||
### DelegatorBond
|
Atom holders may delegate coins to validators; under this circumstance their
|
||||||
|
funds are held in a `Delegation` data structure. It is owned by one
|
||||||
Atom holders may delegate coins to candidates; under this circumstance their
|
delegator, and is associated with the shares for one validator. The sender of
|
||||||
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
|
|
||||||
the transaction is the owner of the bond.
|
the transaction is the owner of the bond.
|
||||||
|
|
||||||
``` go
|
- stored object:
|
||||||
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:
|
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type QueueElem struct {
|
type Delegation struct {
|
||||||
Candidate crypto.PubKey
|
DelegatorAddr sdk.Address // delegation owner address
|
||||||
InitTime int64 // when the element was added to the queue
|
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.
|
### UnbondingDelegation
|
||||||
Every tick the head of the queue is checked and if the unbonding period has
|
- index: delegation address
|
||||||
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.
|
|
||||||
|
|
||||||
### 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
|
```golang
|
||||||
type QueueElemUnbondDelegation struct {
|
type UnbondingDelegation struct {
|
||||||
QueueElem
|
DelegationKey sdk.Address // key of the delegation
|
||||||
Payout Address // account to pay out to
|
ExpectedTokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding
|
||||||
Tokens coin.Coins // the value in Atoms of the amount of delegator shares which are unbonding
|
StartSlashRatio sdk.Rat // validator slash ratio at unbonding initiation
|
||||||
StartSlashRatio rational.Rat // candidate slash ratio
|
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
|
```golang
|
||||||
type QueueElemReDelegate struct {
|
type Redelegation struct {
|
||||||
QueueElem
|
SourceDelegation sdk.Address // source delegation key
|
||||||
Payout Address // account to pay out to
|
DestinationDelegation sdk.Address // destination delegation key
|
||||||
Shares rational.Rat // amount of shares which are unbonding
|
SourceShares sdk.Rat // amount of source shares redelegating
|
||||||
NewCandidate crypto.PubKey // validator to bond to after unbond
|
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
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,67 +1,61 @@
|
||||||
|
|
||||||
### Transaction Overview
|
### Transaction Overview
|
||||||
|
|
||||||
Available Transactions:
|
In this section we describe the processing of the transactions and the
|
||||||
* TxDeclareCandidacy
|
corresponding updates to the state. Transactions:
|
||||||
* TxEditCandidacy
|
- TxCreateValidator
|
||||||
* TxDelegate
|
- TxEditValidator
|
||||||
* TxUnbond
|
- TxDelegation
|
||||||
* TxRedelegate
|
- TxStartUnbonding
|
||||||
* TxProveLive
|
- TxCompleteUnbonding
|
||||||
|
- TxRedelegate
|
||||||
|
- TxCompleteRedelegation
|
||||||
|
|
||||||
## Transaction processing
|
Other important state changes:
|
||||||
|
- Update Validators
|
||||||
|
|
||||||
In this section we describe the processing of the transactions and the
|
Other notes:
|
||||||
corresponding updates to the global state. In the following text we will use
|
- `tx` denotes a reference to the transaction being processed
|
||||||
`gs` to refer to the `GlobalState` data structure, `unbondDelegationQueue` is a
|
- `sender` denotes the address of the sender of the transaction
|
||||||
reference to the queue of unbond delegations, `reDelegationQueue` is the
|
- `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and
|
||||||
reference for the queue of redelegations. We use `tx` to denote a
|
modify objects from the store
|
||||||
reference to a transaction that is being processed, and `sender` to denote the
|
- `sdk.Rat` refers to a rational numeric type specified by the SDK.
|
||||||
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.
|
|
||||||
|
|
||||||
### TxDeclareCandidacy
|
### TxCreateValidator
|
||||||
|
|
||||||
A validator candidacy is declared using the `TxDeclareCandidacy` transaction.
|
A validator is created using the `TxCreateValidator` transaction.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type TxDeclareCandidacy struct {
|
type TxCreateValidator struct {
|
||||||
|
OwnerAddr sdk.Address
|
||||||
ConsensusPubKey crypto.PubKey
|
ConsensusPubKey crypto.PubKey
|
||||||
Amount coin.Coin
|
|
||||||
GovernancePubKey crypto.PubKey
|
GovernancePubKey crypto.PubKey
|
||||||
Commission rational.Rat
|
SelfDelegation coin.Coin
|
||||||
CommissionMax int64
|
|
||||||
CommissionMaxChange int64
|
|
||||||
Description Description
|
Description Description
|
||||||
|
Commission sdk.Rat
|
||||||
|
CommissionMax sdk.Rat
|
||||||
|
CommissionMaxChange sdk.Rat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declareCandidacy(tx TxDeclareCandidacy):
|
createValidator(tx TxCreateValidator):
|
||||||
candidate = loadCandidate(store, tx.PubKey)
|
validator = getValidator(tx.OwnerAddr)
|
||||||
if candidate != nil return // candidate with that public key already exists
|
if validator != nil return // only one validator per address
|
||||||
|
|
||||||
candidate = NewCandidate(tx.PubKey)
|
validator = NewValidator(OwnerAddr, ConsensusPubKey, GovernancePubKey, Description)
|
||||||
candidate.Status = Unbonded
|
init validator poolShares, delegatorShares set to 0
|
||||||
candidate.Owner = sender
|
init validator commision fields from tx
|
||||||
init candidate VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero
|
validator.PoolShares = 0
|
||||||
init commision related fields based on the values from tx
|
|
||||||
candidate.ProposerRewardPool = Coin(0)
|
|
||||||
candidate.Description = tx.Description
|
|
||||||
|
|
||||||
saveCandidate(store, candidate)
|
setValidator(validator)
|
||||||
|
|
||||||
txDelegate = TxDelegate(tx.PubKey, tx.Amount)
|
txDelegate = TxDelegate(tx.OwnerAddr, tx.OwnerAddr, tx.SelfDelegation)
|
||||||
return delegateWithCandidate(txDelegate, candidate)
|
delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate)
|
||||||
|
return
|
||||||
// see delegateWithCandidate function in [TxDelegate](TxDelegate)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### TxEditCandidacy
|
### TxEditValidator
|
||||||
|
|
||||||
If either the `Description` (excluding `DateBonded` which is constant),
|
If either the `Description` (excluding `DateBonded` which is constant),
|
||||||
`Commission`, or the `GovernancePubKey` need to be updated, the
|
`Commission`, or the `GovernancePubKey` need to be updated, the
|
||||||
|
@ -70,214 +64,268 @@ If either the `Description` (excluding `DateBonded` which is constant),
|
||||||
```golang
|
```golang
|
||||||
type TxEditCandidacy struct {
|
type TxEditCandidacy struct {
|
||||||
GovernancePubKey crypto.PubKey
|
GovernancePubKey crypto.PubKey
|
||||||
Commission int64
|
Commission sdk.Rat
|
||||||
Description Description
|
Description Description
|
||||||
}
|
}
|
||||||
|
|
||||||
editCandidacy(tx TxEditCandidacy):
|
editCandidacy(tx TxEditCandidacy):
|
||||||
candidate = loadCandidate(store, tx.PubKey)
|
validator = getValidator(tx.ValidatorAddr)
|
||||||
if candidate == nil or candidate.Status == Revoked return
|
|
||||||
|
|
||||||
if tx.GovernancePubKey != nil candidate.GovernancePubKey = tx.GovernancePubKey
|
if tx.Commission > CommissionMax || tx.Commission < 0 then fail
|
||||||
if tx.Commission >= 0 candidate.Commission = tx.Commission
|
if rateChange(tx.Commission) > CommissionMaxChange then fail
|
||||||
if tx.Description != nil candidate.Description = tx.Description
|
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
|
return
|
||||||
```
|
```
|
||||||
|
|
||||||
### TxDelegate
|
### TxDelegation
|
||||||
|
|
||||||
Delegator bonds are created using the `TxDelegate` transaction. Within this
|
Within this transaction the delegator provides coins, and in return receives
|
||||||
transaction the delegator provides an amount of coins, and in return receives
|
some amount of their validator's delegator-shares that are assigned to
|
||||||
some amount of candidate's delegator shares that are assigned to
|
`Delegation.Shares`.
|
||||||
`DelegatorBond.Shares`.
|
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type TxDelegate struct {
|
type TxDelegate struct {
|
||||||
PubKey crypto.PubKey
|
DelegatorAddr sdk.Address
|
||||||
Amount coin.Coin
|
ValidatorAddr sdk.Address
|
||||||
|
Amount sdk.Coin
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate(tx TxDelegate):
|
delegate(tx TxDelegate):
|
||||||
candidate = loadCandidate(store, tx.PubKey)
|
pool = getPool()
|
||||||
if candidate == nil return
|
if validator.Status == Revoked return
|
||||||
return delegateWithCandidate(tx, candidate)
|
|
||||||
|
|
||||||
delegateWithCandidate(tx TxDelegate, candidate Candidate):
|
delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr)
|
||||||
if candidate.Status == Revoked return
|
if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr)
|
||||||
|
|
||||||
if candidate.Status == Bonded
|
|
||||||
poolAccount = params.HoldBonded
|
|
||||||
else
|
|
||||||
poolAccount = params.HoldUnbonded
|
|
||||||
|
|
||||||
err = transfer(sender, poolAccount, tx.Amount)
|
validator, pool, issuedDelegatorShares = validator.addTokensFromDel(tx.Amount, pool)
|
||||||
if err != nil return
|
delegation.Shares += issuedDelegatorShares
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if candidate.IssuedDelegatorShares.IsZero()
|
setDelegation(delegation)
|
||||||
exRate = rational.One
|
updateValidator(validator)
|
||||||
else
|
setPool(pool)
|
||||||
exRate = candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
|
return
|
||||||
|
|
||||||
issuedDelegatorShares = issuedShares / exRate
|
|
||||||
candidate.IssuedDelegatorShares += issuedDelegatorShares
|
|
||||||
return issuedDelegatorShares
|
|
||||||
|
|
||||||
exchangeRate(shares rational.Rat, tokenAmount int64):
|
|
||||||
if shares.IsZero() then return rational.One
|
|
||||||
return tokenAmount / shares
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### TxUnbond
|
### TxStartUnbonding
|
||||||
|
|
||||||
Delegator unbonding is defined with the following transaction:
|
Delegator unbonding is defined with the following transaction:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type TxUnbond struct {
|
type TxStartUnbonding struct {
|
||||||
PubKey crypto.PubKey
|
DelegatorAddr sdk.Address
|
||||||
Shares rational.Rat
|
ValidatorAddr sdk.Address
|
||||||
|
Shares string
|
||||||
}
|
}
|
||||||
|
|
||||||
unbond(tx TxUnbond):
|
startUnbonding(tx TxStartUnbonding):
|
||||||
bond = loadDelegatorBond(store, sender, tx.PubKey)
|
delegation, found = getDelegatorBond(store, sender, tx.PubKey)
|
||||||
if bond == nil return
|
if !found == 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)
|
|
||||||
|
|
||||||
if revokeCandidacy
|
if bond.Shares < tx.Shares
|
||||||
if candidate.Status == Bonded then bondedToUnbondedPool(candidate)
|
return ErrNotEnoughBondShares
|
||||||
candidate.Status = Revoked
|
|
||||||
|
|
||||||
if candidate.IssuedDelegatorShares.IsZero()
|
validator, found = GetValidator(tx.ValidatorAddr)
|
||||||
removeCandidate(store, tx.PubKey)
|
if !found {
|
||||||
else
|
return err
|
||||||
saveCandidate(store, candidate)
|
|
||||||
|
|
||||||
saveGlobalState(store, gs)
|
bond.Shares -= tx.Shares
|
||||||
return
|
|
||||||
|
|
||||||
removeShares(candidate Candidate, shares rational.Rat):
|
revokeCandidacy = false
|
||||||
globalPoolSharesToRemove = delegatorShareExRate(candidate) * shares
|
if bond.Shares.IsZero() {
|
||||||
|
|
||||||
if candidate.Status == Bonded
|
if bond.DelegatorAddr == validator.Owner && validator.Revoked == false
|
||||||
gs.BondedShares -= globalPoolSharesToRemove
|
revokeCandidacy = true
|
||||||
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
|
|
||||||
|
|
||||||
delegatorShareExRate(candidate Candidate):
|
removeDelegation( bond)
|
||||||
if candidate.IssuedDelegatorShares.IsZero() then return rational.One
|
else
|
||||||
return candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
|
bond.Height = currentBlockHeight
|
||||||
|
setDelegation(bond)
|
||||||
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
|
|
||||||
|
|
||||||
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
|
Complete the unbonding and transfer the coins to the delegate. Perform any
|
||||||
receiving equal reward to as if they had never unbonded.
|
slashing that occured during the unbonding period.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type TxRedelegate struct {
|
type TxUnbondingComplete struct {
|
||||||
PubKeyFrom crypto.PubKey
|
DelegatorAddr sdk.Address
|
||||||
PubKeyTo crypto.PubKey
|
ValidatorAddr sdk.Address
|
||||||
Shares rational.Rat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redelegate(tx TxRedelegate):
|
redelegationComplete(tx TxRedelegate):
|
||||||
bond = loadDelegatorBond(store, sender, tx.PubKey)
|
unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator)
|
||||||
if bond == nil then return
|
if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight
|
||||||
|
validator = GetValidator(tx.ValidatorAddr)
|
||||||
if bond.Shares < tx.Shares return
|
returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio
|
||||||
candidate = loadCandidate(store, tx.PubKeyFrom)
|
AddCoins(unbonding.DelegatorAddr, returnTokens)
|
||||||
if candidate == nil return
|
removeUnbondingDelegation(unbonding)
|
||||||
|
|
||||||
candidate.RedelegatingShares += tx.Shares
|
|
||||||
reDelegationElem = QueueElemReDelegate(tx.PubKeyFrom, currentHeight(), sender, tx.Shares, tx.PubKeyTo)
|
|
||||||
redelegationQueue.add(reDelegationElem)
|
|
||||||
return
|
return
|
||||||
```
|
```
|
||||||
|
|
||||||
### TxProveLive
|
### TxRedelegation
|
||||||
|
|
||||||
If a validator was automatically unbonded due to liveness issues and wishes to
|
The redelegation command allows delegators to instantly switch validators. Once
|
||||||
assert it is still online, it can send `TxProveLive`:
|
the unbonding period has passed, the redelegation must be completed with
|
||||||
|
txRedelegationComplete.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type TxProveLive struct {
|
type TxRedelegate struct {
|
||||||
PubKey crypto.PubKey
|
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
|
### TxCompleteRedelegation
|
||||||
transacted to move will be bonded back to the now-live validator and begin to
|
|
||||||
once again collect provisions and rewards.
|
|
||||||
|
|
||||||
|
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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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
|
|
||||||
```
|
|
|
@ -50,7 +50,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the ABCI server
|
// 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 {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
- name: Gather status
|
- name: Gather status
|
||||||
uri:
|
uri:
|
||||||
body_format: json
|
body_format: json
|
||||||
url: "http://{{inventory_hostname}}:46657/status"
|
url: "http://{{inventory_hostname}}:26657/status"
|
||||||
register: status
|
register: status
|
||||||
|
|
||||||
- name: Print status
|
- name: Print status
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -310,7 +310,7 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) (
|
||||||
if len(persistentPeers) == 0 {
|
if len(persistentPeers) == 0 {
|
||||||
comma = ""
|
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
|
return
|
||||||
|
|
|
@ -37,7 +37,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
||||||
|
|
||||||
// basic flags for abci app
|
// basic flags for abci app
|
||||||
cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint")
|
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
|
// AddNodeFlags adds support for all tendermint-specific command line options
|
||||||
tcmd.AddNodeFlags(cmd)
|
tcmd.AddNodeFlags(cmd)
|
||||||
|
|
|
@ -72,7 +72,7 @@ func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
|
||||||
Short: "Reset blockchain database, priv_validator.json file, and the logger",
|
Short: "Reset blockchain database, priv_validator.json file, and the logger",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg := ctx.Config
|
cfg := ctx.Config
|
||||||
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), ctx.Logger)
|
tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,65 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
amino "github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
|
tmclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Uses localhost
|
// Wait for the next tendermint block from the Tendermint RPC
|
||||||
func WaitForHeight(height int64, port string) {
|
// 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 {
|
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
|
// get url, try a few times
|
||||||
var res *http.Response
|
var res *http.Response
|
||||||
var err error
|
var err error
|
||||||
|
@ -25,7 +74,7 @@ func WaitForHeight(height int64, port string) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Millisecond * 200)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -45,7 +94,8 @@ func WaitForHeight(height int64, port string) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resultBlock.Block.Height >= height {
|
if resultBlock.Block != nil &&
|
||||||
|
resultBlock.Block.Height >= height {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"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
|
v := Version
|
||||||
if GitCommit != "" {
|
if GitCommit != "" {
|
||||||
v = v + "-" + GitCommit
|
v = v + "-" + GitCommit
|
||||||
|
@ -26,12 +26,6 @@ func getVersion() string {
|
||||||
|
|
||||||
// CMD
|
// CMD
|
||||||
func printVersion(cmd *cobra.Command, args []string) {
|
func printVersion(cmd *cobra.Command, args []string) {
|
||||||
v := getVersion()
|
v := GetVersion()
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// version REST handler endpoint
|
|
||||||
func RequestHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := getVersion()
|
|
||||||
w.Write([]byte(v))
|
|
||||||
}
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
|
||||||
|
|
||||||
// perform query
|
// perform query
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := ctx.Query(auth.AddressStoreKey(key), storeName)
|
res, err := ctx.QueryStore(auth.AddressStoreKey(key), storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder aut
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := ctx.Query(auth.AddressStoreKey(addr), storeName)
|
res, err := ctx.QueryStore(auth.AddressStoreKey(addr), storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(fmt.Sprintf("couldn't query account. Error: %s", err.Error())))
|
w.Write([]byte(fmt.Sprintf("couldn't query account. Error: %s", err.Error())))
|
||||||
|
|
|
@ -58,7 +58,7 @@ I[04-02|14:09:14.453] Generated genesis file module=main p
|
||||||
}
|
}
|
||||||
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
|
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
|
||||||
> ID2=test-chain-4XHTPn
|
> ID2=test-chain-4XHTPn
|
||||||
> NODE2=tcp://0.0.0.0:46657
|
> NODE2=tcp://0.0.0.0:26657
|
||||||
> basecli keys add key2 --recover
|
> basecli keys add key2 --recover
|
||||||
Enter a passphrase for your key:
|
Enter a passphrase for your key:
|
||||||
Repeat the passphrase:
|
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 ~/.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
|
## Check balance
|
||||||
|
|
|
@ -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(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(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")
|
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) {
|
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 {
|
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {
|
||||||
|
|
|
@ -27,7 +27,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||||
}
|
}
|
||||||
key := slashing.GetValidatorSigningInfoKey(pk.Address())
|
key := slashing.GetValidatorSigningInfoKey(pk.Address())
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := ctx.Query(key, storeName)
|
res, err := ctx.QueryStore(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||||
}
|
}
|
||||||
key := stake.GetValidatorKey(addr)
|
key := stake.GetValidatorKey(addr)
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := ctx.Query(key, storeName)
|
res, err := ctx.QueryStore(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||||
|
|
||||||
key := stake.GetDelegationKey(delAddr, addr, cdc)
|
key := stake.GetDelegationKey(delAddr, addr, cdc)
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := ctx.Query(key, storeName)
|
res, err := ctx.QueryStore(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
||||||
|
|
||||||
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
||||||
|
|
||||||
res, err := ctx.Query(key, storeName)
|
res, err := ctx.QueryStore(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))
|
w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
// Delegation represents the bond with tokens held by an account. It is
|
// 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
|
// owned by one delegator, and is associated with the voting power of one
|
||||||
// pubKey.
|
// pubKey.
|
||||||
// TODO better way of managing space
|
|
||||||
type Delegation struct {
|
type Delegation struct {
|
||||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||||
|
@ -50,5 +49,4 @@ func (b Delegation) HumanReadableString() (string, error) {
|
||||||
resp += fmt.Sprintf("Height: %d", b.Height)
|
resp += fmt.Sprintf("Height: %d", b.Height)
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,16 +294,14 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator
|
||||||
return validator
|
return validator
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX TODO build in consideration for revoked
|
|
||||||
//
|
|
||||||
// Update the validator group and kick out any old validators. In addition this
|
// 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
|
// function adds (or doesn't add) a validator which has updated its bonded
|
||||||
// tokens to the validator group. -> this validator is specified through the
|
// tokens to the validator group. -> this validator is specified through the
|
||||||
// updatedValidatorAddr term.
|
// updatedValidatorAddr term.
|
||||||
//
|
//
|
||||||
// The correct subset is retrieved by iterating through an index of the
|
// The correct subset is retrieved by iterating through an index of the
|
||||||
// validators sorted by power, stored using the ValidatorsByPowerKey. Simultaniously
|
// validators sorted by power, stored using the ValidatorsByPowerKey.
|
||||||
// the current validator records are updated in store with the
|
// Simultaneously the current validator records are updated in store with the
|
||||||
// ValidatorsBondedKey. This store is used to determine if a validator is a
|
// ValidatorsBondedKey. This store is used to determine if a validator is a
|
||||||
// validator without needing to iterate over the subspace as we do in
|
// validator without needing to iterate over the subspace as we do in
|
||||||
// GetValidators.
|
// GetValidators.
|
||||||
|
@ -331,10 +329,10 @@ func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore,
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// either retrieve the original validator from the store,
|
// either retrieve the original validator from the store, or under the
|
||||||
// or under the situation that this is the "new validator" just
|
// situation that this is the "new validator" just use the validator
|
||||||
// use the validator provided because it has not yet been updated
|
// provided because it has not yet been updated in the main validator
|
||||||
// in the main validator store
|
// store
|
||||||
ownerAddr := iterator.Value()
|
ownerAddr := iterator.Value()
|
||||||
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
if bytes.Equal(ownerAddr, newValidator.Owner) {
|
||||||
validator = newValidator
|
validator = newValidator
|
||||||
|
|
|
@ -4,9 +4,6 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// kind of shares
|
|
||||||
type PoolShareKind byte
|
|
||||||
|
|
||||||
// pool shares held by a validator
|
// pool shares held by a validator
|
||||||
type PoolShares struct {
|
type PoolShares struct {
|
||||||
Status sdk.BondStatus `json:"status"`
|
Status sdk.BondStatus `json:"status"`
|
||||||
|
|
|
@ -82,10 +82,10 @@ func (v Validator) equal(c2 Validator) bool {
|
||||||
|
|
||||||
// Description - description fields for a validator
|
// Description - description fields for a validator
|
||||||
type Description struct {
|
type Description struct {
|
||||||
Moniker string `json:"moniker"`
|
Moniker string `json:"moniker"` // name
|
||||||
Identity string `json:"identity"`
|
Identity string `json:"identity"` // optional identity signature (ex. UPort or Keybase)
|
||||||
Website string `json:"website"`
|
Website string `json:"website"` // optional website link
|
||||||
Details string `json:"details"`
|
Details string `json:"details"` // optional details
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDescription(moniker, identity, website, details string) Description {
|
func NewDescription(moniker, identity, website, details string) Description {
|
||||||
|
|
Loading…
Reference in New Issue