Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution
This commit is contained in:
commit
fc9bafb8d3
|
@ -136,6 +136,24 @@ jobs:
|
||||||
export PATH="$GOBIN:$PATH"
|
export PATH="$GOBIN:$PATH"
|
||||||
make test_sim_gaia_fast
|
make test_sim_gaia_fast
|
||||||
|
|
||||||
|
test_sim_gaia_multi_seed:
|
||||||
|
<<: *defaults
|
||||||
|
parallelism: 1
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: dependencies
|
||||||
|
command: |
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make get_vendor_deps
|
||||||
|
- run:
|
||||||
|
name: Test multi-seed Gaia simulation
|
||||||
|
command: |
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make test_sim_gaia_multi_seed
|
||||||
|
|
||||||
test_cover:
|
test_cover:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
parallelism: 4
|
parallelism: 4
|
||||||
|
@ -240,6 +258,9 @@ workflows:
|
||||||
- test_sim_gaia_fast:
|
- test_sim_gaia_fast:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
|
- test_sim_gaia_multi_seed:
|
||||||
|
requires:
|
||||||
|
- setup_dependencies
|
||||||
- test_cover:
|
- test_cover:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -162,9 +162,9 @@ test_sim_gaia_fast:
|
||||||
@echo "Running quick Gaia simulation. This may take several minutes..."
|
@echo "Running quick Gaia simulation. This may take several minutes..."
|
||||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h
|
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h
|
||||||
|
|
||||||
test_sim_gaia_full:
|
test_sim_gaia_multi_seed:
|
||||||
@echo "Running full multi-seed Gaia simulation. This may take awhile!"
|
@echo "Running multi-seed Gaia simulation. This may take awhile!"
|
||||||
@sh scripts/multisim.sh
|
@bash scripts/multisim.sh 10
|
||||||
|
|
||||||
SIM_NUM_BLOCKS ?= 210
|
SIM_NUM_BLOCKS ?= 210
|
||||||
SIM_BLOCK_SIZE ?= 200
|
SIM_BLOCK_SIZE ?= 200
|
||||||
|
@ -241,4 +241,4 @@ localnet-stop:
|
||||||
check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \
|
check_tools check_dev_tools get_tools get_dev_tools get_vendor_deps draw_deps test test_cli test_unit \
|
||||||
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
|
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
|
||||||
build-linux build-docker-gaiadnode localnet-start localnet-stop \
|
build-linux build-docker-gaiadnode localnet-start localnet-stop \
|
||||||
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_slow update_tools update_dev_tools
|
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast test_sim_gaia_multi_seed update_tools update_dev_tools
|
||||||
|
|
|
@ -28,6 +28,7 @@ BREAKING CHANGES
|
||||||
* [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed"
|
* [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed"
|
||||||
* [x/stake] [#1676] Revoked and jailed validators put into the unbonding state
|
* [x/stake] [#1676] Revoked and jailed validators put into the unbonding state
|
||||||
* [x/stake] [#1877] Redelegations/unbonding-delegation from unbonding validator have reduced time
|
* [x/stake] [#1877] Redelegations/unbonding-delegation from unbonding validator have reduced time
|
||||||
|
* [x/slashing] \#1789 Slashing changes for Tendermint validator set offset (NextValSet)
|
||||||
* [x/stake] [\#2040](https://github.com/cosmos/cosmos-sdk/issues/2040) Validator
|
* [x/stake] [\#2040](https://github.com/cosmos/cosmos-sdk/issues/2040) Validator
|
||||||
operator type has now changed to `sdk.ValAddress`
|
operator type has now changed to `sdk.ValAddress`
|
||||||
* [x/stake] [\#2221](https://github.com/cosmos/cosmos-sdk/issues/2221) New
|
* [x/stake] [\#2221](https://github.com/cosmos/cosmos-sdk/issues/2221) New
|
||||||
|
@ -41,6 +42,7 @@ BREAKING CHANGES
|
||||||
* [x/gov] [#2195] Governance uses BFT Time
|
* [x/gov] [#2195] Governance uses BFT Time
|
||||||
* [x/gov] \#2256 Removed slashing for governance non-voting validators
|
* [x/gov] \#2256 Removed slashing for governance non-voting validators
|
||||||
* [simulation] \#2162 Added back correct supply invariants
|
* [simulation] \#2162 Added back correct supply invariants
|
||||||
|
* [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing
|
||||||
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker
|
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker
|
||||||
|
|
||||||
* SDK
|
* SDK
|
||||||
|
@ -88,7 +90,7 @@ FEATURES
|
||||||
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
||||||
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
||||||
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
||||||
* [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers`
|
* [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers`, rename `/accounts/{address}` to `/auth/accounts/{address}`
|
||||||
|
|
||||||
* Gaia CLI (`gaiacli`)
|
* Gaia CLI (`gaiacli`)
|
||||||
* [cli] Cmds to query staking pool and params
|
* [cli] Cmds to query staking pool and params
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||||
name = "inmemorykey"
|
name = "inmemorykey"
|
||||||
} else {
|
} else {
|
||||||
if len(args) != 1 || len(args[0]) == 0 {
|
if len(args) != 1 || len(args[0]) == 0 {
|
||||||
return errors.New("you must provide a name for the key")
|
return errMissingName()
|
||||||
}
|
}
|
||||||
name = args[0]
|
name = args[0]
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
|
@ -144,11 +143,16 @@ func printCreate(info keys.Info, seed string) {
|
||||||
if !viper.GetBool(flagNoBackup) {
|
if !viper.GetBool(flagNoBackup) {
|
||||||
out.Seed = seed
|
out.Seed = seed
|
||||||
}
|
}
|
||||||
json, err := MarshalJSON(out)
|
var jsonString []byte
|
||||||
|
if viper.GetBool(client.FlagIndentResponse) {
|
||||||
|
jsonString, err = cdc.MarshalJSONIndent(out, "", " ")
|
||||||
|
} else {
|
||||||
|
jsonString, err = cdc.MarshalJSON(out)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // really shouldn't happen...
|
panic(err) // really shouldn't happen...
|
||||||
}
|
}
|
||||||
fmt.Println(string(json))
|
fmt.Println(string(jsonString))
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("I can't speak: %s", output))
|
panic(fmt.Sprintf("I can't speak: %s", output))
|
||||||
}
|
}
|
||||||
|
@ -165,20 +169,25 @@ type NewKeyBody struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new key REST handler
|
// add new key REST handler
|
||||||
func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func AddNewKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var kb keys.Keybase
|
var kb keys.Keybase
|
||||||
var m NewKeyBody
|
var m NewKeyBody
|
||||||
|
|
||||||
kb, err := GetKeyBase()
|
kb, err := GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
err = json.Unmarshal(body, &m)
|
err = json.Unmarshal(body, &m)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
@ -186,21 +195,24 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if m.Name == "" {
|
if m.Name == "" {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte("You have to specify a name for the locally stored account."))
|
err = errMissingName()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if m.Password == "" {
|
if m.Password == "" {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte("You have to specify a password for the locally stored account."))
|
err = errMissingPassword()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if already exists
|
// check if already exists
|
||||||
infos, err := kb.List()
|
infos, err := kb.List()
|
||||||
for _, i := range infos {
|
for _, info := range infos {
|
||||||
if i.GetName() == m.Name {
|
if info.GetName() == m.Name {
|
||||||
w.WriteHeader(http.StatusConflict)
|
w.WriteHeader(http.StatusConflict)
|
||||||
w.Write([]byte(fmt.Sprintf("Account with name %s already exists.", m.Name)))
|
err = errKeyNameConflict(m.Name)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,14 +238,8 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
keyOutput.Seed = seed
|
keyOutput.Seed = seed
|
||||||
|
|
||||||
bz, err := json.Marshal(keyOutput)
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(bz)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// function to just a new seed to display in the UI before actually persisting it in the keybase
|
// function to just a new seed to display in the UI before actually persisting it in the keybase
|
||||||
|
@ -258,3 +264,82 @@ func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
seed := getSeed(algo)
|
seed := getSeed(algo)
|
||||||
w.Write([]byte(seed))
|
w.Write([]byte(seed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecoverKeyBody is recover key request REST body
|
||||||
|
type RecoverKeyBody struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
Seed string `json:"seed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoverRequestHandler performs key recover request
|
||||||
|
func RecoverRequestHandler(indent bool) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
name := vars["name"]
|
||||||
|
var m RecoverKeyBody
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cdc.UnmarshalJSON(body, &m)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingName()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Password == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingPassword()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Seed == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingSeed()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kb, err := GetKeyBase()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check if already exists
|
||||||
|
infos, err := kb.List()
|
||||||
|
for _, info := range infos {
|
||||||
|
if info.GetName() == name {
|
||||||
|
w.WriteHeader(http.StatusConflict)
|
||||||
|
err = errKeyNameConflict(name)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := kb.CreateKey(name, m.Seed, m.Password)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOutput, err := Bech32KeyOutput(info)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -68,14 +68,14 @@ func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&m)
|
err := decoder.Decode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,10 @@ func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO handle error if key is not available or pass is wrong
|
// TODO handle error if key is not available or pass is wrong
|
||||||
err = kb.Delete(name, m.Password)
|
err = kb.Delete(name, m.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package keys
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func errKeyNameConflict(name string) error {
|
||||||
|
return fmt.Errorf("acount with name %s already exists", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingName() error {
|
||||||
|
return fmt.Errorf("you have to specify a name for the locally stored account")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingPassword() error {
|
||||||
|
return fmt.Errorf("you have to specify a password for the locally stored account")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingSeed() error {
|
||||||
|
return fmt.Errorf("you have to specify seed for key recover")
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -35,35 +34,31 @@ func runListCmd(cmd *cobra.Command, args []string) error {
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
// query key list REST handler
|
// query key list REST handler
|
||||||
func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func QueryKeysRequestHandler(indent bool) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
kb, err := GetKeyBase()
|
kb, err := GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
infos, err := kb.List()
|
infos, err := kb.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// an empty list will be JSONized as null, but we want to keep the empty list
|
// an empty list will be JSONized as null, but we want to keep the empty list
|
||||||
if len(infos) == 0 {
|
if len(infos) == 0 {
|
||||||
w.Write([]byte("[]"))
|
PostProcessResponse(w, cdc, "[]", indent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keysOutput, err := Bech32KeysOutput(infos)
|
keysOutput, err := Bech32KeysOutput(infos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
output, err := json.MarshalIndent(keysOutput, "", " ")
|
PostProcessResponse(w, cdc, keysOutput, indent)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,12 @@ func Commands() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// resgister REST routes
|
// resgister REST routes
|
||||||
func RegisterRoutes(r *mux.Router) {
|
func RegisterRoutes(r *mux.Router, indent bool) {
|
||||||
r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET")
|
r.HandleFunc("/keys", QueryKeysRequestHandler(indent)).Methods("GET")
|
||||||
r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST")
|
r.HandleFunc("/keys", AddNewKeyRequestHandler(indent)).Methods("POST")
|
||||||
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
|
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/keys/{name}", GetKeyRequestHandler).Methods("GET")
|
r.HandleFunc("/keys/{name}/recover", RecoverRequestHandler(indent)).Methods("POST")
|
||||||
|
r.HandleFunc("/keys/{name}", GetKeyRequestHandler(indent)).Methods("GET")
|
||||||
r.HandleFunc("/keys/{name}", UpdateKeyRequestHandler).Methods("PUT")
|
r.HandleFunc("/keys/{name}", UpdateKeyRequestHandler).Methods("PUT")
|
||||||
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
|
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -91,7 +90,8 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
// get key REST handler
|
// get key REST handler
|
||||||
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func GetKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
|
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
|
||||||
|
@ -102,7 +102,7 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
bechKeyOut, err := getBechKeyOut(bechPrefix)
|
bechKeyOut, err := getBechKeyOut(bechPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -111,24 +111,18 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO: check for the error if key actually does not exist, instead of
|
// TODO: check for the error if key actually does not exist, instead of
|
||||||
// assuming this as the reason
|
// assuming this as the reason
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(404)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
keyOutput, err := bechKeyOut(info)
|
keyOutput, err := bechKeyOut(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := json.MarshalIndent(keyOutput, "", " ")
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,14 +69,14 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&m)
|
err := decoder.Decode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,10 +86,10 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO check if account exists and if password is correct
|
// TODO check if account exists and if password is correct
|
||||||
err = kb.Update(name, m.OldPassword, getNewpass)
|
err = kb.Update(name, m.OldPassword, getNewpass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyDBName is the directory under root where we store the keys
|
// KeyDBName is the directory under root where we store the keys
|
||||||
|
@ -231,3 +233,26 @@ func printPubKey(info keys.Info, bechKeyOut bechKeyOutFn) {
|
||||||
|
|
||||||
fmt.Println(ko.PubKey)
|
fmt.Println(ko.PubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostProcessResponse performs post process for rest response
|
||||||
|
func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response interface{}, indent bool) {
|
||||||
|
var output []byte
|
||||||
|
switch response.(type) {
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
if indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(response, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(response)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case []byte:
|
||||||
|
output = response.([]byte)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(output)
|
||||||
|
}
|
||||||
|
|
|
@ -54,9 +54,13 @@ func TestKeys(t *testing.T) {
|
||||||
match := reg.MatchString(seed)
|
match := reg.MatchString(seed)
|
||||||
require.True(t, match, "Returned seed has wrong format", seed)
|
require.True(t, match, "Returned seed has wrong format", seed)
|
||||||
|
|
||||||
|
// recover key
|
||||||
|
recoverName := "test_recovername"
|
||||||
|
recoverPassword := "1234567890"
|
||||||
|
doRecoverKey(t, port, recoverName, recoverPassword, seed)
|
||||||
|
|
||||||
newName := "test_newname"
|
newName := "test_newname"
|
||||||
newPassword := "0987654321"
|
newPassword := "0987654321"
|
||||||
|
|
||||||
// add key
|
// add key
|
||||||
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
|
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
|
||||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||||
|
@ -78,7 +82,7 @@ func TestKeys(t *testing.T) {
|
||||||
// existing keys
|
// existing keys
|
||||||
res, body = Request(t, port, "GET", "/keys", nil)
|
res, body = Request(t, port, "GET", "/keys", nil)
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
var m [2]keys.KeyOutput
|
var m [3]keys.KeyOutput
|
||||||
err = cdc.UnmarshalJSON([]byte(body), &m)
|
err = cdc.UnmarshalJSON([]byte(body), &m)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
@ -243,7 +247,7 @@ func TestCoinSend(t *testing.T) {
|
||||||
someFakeAddr := sdk.AccAddress(bz)
|
someFakeAddr := sdk.AccAddress(bz)
|
||||||
|
|
||||||
// query empty
|
// query empty
|
||||||
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", someFakeAddr), nil)
|
res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", someFakeAddr), nil)
|
||||||
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
||||||
|
|
||||||
acc := getAccount(t, port, addr)
|
acc := getAccount(t, port, addr)
|
||||||
|
@ -518,9 +522,12 @@ func TestBonding(t *testing.T) {
|
||||||
name, password, denom := "test", "1234567890", "steak"
|
name, password, denom := "test", "1234567890", "steak"
|
||||||
addr, seed := CreateAddr(t, name, password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, name, password, GetKeyBase(t))
|
||||||
|
|
||||||
cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(valPubKeys))
|
||||||
|
require.Equal(t, 2, len(operAddrs))
|
||||||
|
|
||||||
amt := sdk.NewDec(60)
|
amt := sdk.NewDec(60)
|
||||||
validator := getValidator(t, port, operAddrs[0])
|
validator := getValidator(t, port, operAddrs[0])
|
||||||
|
|
||||||
|
@ -802,7 +809,7 @@ func TestProposalsQuery(t *testing.T) {
|
||||||
//_____________________________________________________________________________
|
//_____________________________________________________________________________
|
||||||
// get the account to get the sequence
|
// get the account to get the sequence
|
||||||
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
||||||
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", addr), nil)
|
res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr), nil)
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
var acc auth.Account
|
var acc auth.Account
|
||||||
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
||||||
|
@ -856,6 +863,20 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) {
|
||||||
|
jsonStr := []byte(fmt.Sprintf(`{"password":"%s", "seed":"%s"}`, recoverPassword, seed))
|
||||||
|
res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), jsonStr)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
|
var resp keys.KeyOutput
|
||||||
|
err := codec.Cdc.UnmarshalJSON([]byte(body), &resp)
|
||||||
|
require.Nil(t, err, body)
|
||||||
|
|
||||||
|
addr1Bech32 := resp.Address
|
||||||
|
_, err = sdk.AccAddressFromBech32(addr1Bech32)
|
||||||
|
require.NoError(t, err, "Failed to return a correct bech32 address")
|
||||||
|
}
|
||||||
|
|
||||||
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
|
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||||
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
|
|
|
@ -145,7 +145,7 @@ func createHandler(cdc *codec.Codec) *mux.Router {
|
||||||
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
|
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
|
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
|
||||||
|
|
||||||
keys.RegisterRoutes(r)
|
keys.RegisterRoutes(r, cliCtx.Indent)
|
||||||
rpc.RegisterRoutes(cliCtx, r)
|
rpc.RegisterRoutes(cliCtx, r)
|
||||||
tx.RegisterRoutes(cliCtx, r, cdc)
|
tx.RegisterRoutes(cliCtx, r, cdc)
|
||||||
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
|
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
|
||||||
|
|
|
@ -346,6 +346,184 @@ paths:
|
||||||
$ref: "#/definitions/BroadcastTxCommitResult"
|
$ref: "#/definitions/BroadcastTxCommitResult"
|
||||||
400:
|
400:
|
||||||
description: The Tx was malformated
|
description: The Tx was malformated
|
||||||
|
/keys:
|
||||||
|
get:
|
||||||
|
summary: List of accounts stored locally
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Array of accounts
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Account'
|
||||||
|
post:
|
||||||
|
summary: Create a new account locally
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: account
|
||||||
|
description: The account to create
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- password
|
||||||
|
- seed
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
seed:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Returns account information of the created key
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Account"
|
||||||
|
/keys/seed:
|
||||||
|
get:
|
||||||
|
summary: Create a new seed to create a new account with
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: 16 word Seed
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
/keys/{name}/recover:
|
||||||
|
post:
|
||||||
|
summary: Recover a account from a seed
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: name
|
||||||
|
description: Account name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: body
|
||||||
|
name: pwdAndSeed
|
||||||
|
description: Provide password and seed to recover a key
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- password
|
||||||
|
- seed
|
||||||
|
properties:
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
seed:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Returns account information of the recovered key
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Account"
|
||||||
|
/keys/{name}:
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: name
|
||||||
|
description: Account name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
get:
|
||||||
|
summary: Get a certain locally stored account
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Locally stored account
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Account"
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
put:
|
||||||
|
summary: Update the password for this account in the KMS
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: account
|
||||||
|
description: The new and old password
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- new_password
|
||||||
|
- old_password
|
||||||
|
properties:
|
||||||
|
new_password:
|
||||||
|
type: string
|
||||||
|
old_password:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Updated password
|
||||||
|
401:
|
||||||
|
description: Password is wrong
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
delete:
|
||||||
|
summary: Remove an account
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: account
|
||||||
|
description: The password of the account to remove from the KMS
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- password
|
||||||
|
properties:
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Removed account
|
||||||
|
401:
|
||||||
|
description: Password is wrong
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
/auth/accounts/{address}:
|
||||||
|
get:
|
||||||
|
summary: Get the account information on blockchain
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: address
|
||||||
|
description: Account address
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Account information on the blockchain
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/AccountQueryResponse"
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
|
||||||
definitions:
|
definitions:
|
||||||
CheckTxResult:
|
CheckTxResult:
|
||||||
|
@ -561,7 +739,35 @@ definitions:
|
||||||
address:
|
address:
|
||||||
$ref: "#/definitions/Address"
|
$ref: "#/definitions/Address"
|
||||||
pub_key:
|
pub_key:
|
||||||
$ref: "#/definitions/PubKey"
|
type: string
|
||||||
|
example: "cosmospub1addwnpepqfgv3pakxazq2fgs8tmmhmzsrs94fptl7kyztyxprjpf0mkus3h7cxqe70s"
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
example: local
|
||||||
|
seed:
|
||||||
|
type: string
|
||||||
|
AccountInfo:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
account_number:
|
||||||
|
type: string
|
||||||
|
address:
|
||||||
|
type: string
|
||||||
|
coins:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Coin"
|
||||||
|
public_key:
|
||||||
|
type: string
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
AccountQueryResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
$ref: "#/definitions/AccountInfo"
|
||||||
BlockID:
|
BlockID:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -146,7 +146,7 @@ func InitializeTestLCD(
|
||||||
// append initial (proposing) validator
|
// append initial (proposing) validator
|
||||||
genDoc.Validators[0] = tmtypes.GenesisValidator{
|
genDoc.Validators[0] = tmtypes.GenesisValidator{
|
||||||
PubKey: privVal.GetPubKey(),
|
PubKey: privVal.GetPubKey(),
|
||||||
Power: 999999, // create enough power to enable 2/3 voting power
|
Power: 100, // create enough power to enable 2/3 voting power
|
||||||
Name: "validator-1",
|
Name: "validator-1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ func InitializeTestLCD(
|
||||||
appGenTxs = append(appGenTxs, appGenTx)
|
appGenTxs = append(appGenTxs, appGenTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
|
genesisState, err := gapp.NewTestGaiaAppGenState(cdc, appGenTxs[:], genDoc.Validators, valOperAddrs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// add some tokens to init accounts
|
// add some tokens to init accounts
|
||||||
|
|
|
@ -11,10 +11,10 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO these next two functions feel kinda hacky based on their placement
|
// TODO these next two functions feel kinda hacky based on their placement
|
||||||
|
|
|
@ -162,7 +162,6 @@ func GaiaAppGenTxNF(cdc *codec.Codec, pk crypto.PubKey, addr sdk.AccAddress, nam
|
||||||
// Create the core parameters for genesis initialization for gaia
|
// Create the core parameters for genesis initialization for gaia
|
||||||
// note that the pubkey input is this machines pubkey
|
// note that the pubkey input is this machines pubkey
|
||||||
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
|
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
|
||||||
|
|
||||||
if len(appGenTxs) == 0 {
|
if len(appGenTxs) == 0 {
|
||||||
err = errors.New("must provide at least genesis transaction")
|
err = errors.New("must provide at least genesis transaction")
|
||||||
return
|
return
|
||||||
|
@ -198,6 +197,7 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat
|
||||||
DistrData: distr.DefaultGenesisState(),
|
DistrData: distr.DefaultGenesisState(),
|
||||||
GovData: gov.DefaultGenesisState(),
|
GovData: gov.DefaultGenesisState(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTestGaiaAppGenState creates the core parameters for a test genesis
|
||||||
|
// initialization given a set of genesis txs, TM validators and their respective
|
||||||
|
// operating addresses.
|
||||||
|
func NewTestGaiaAppGenState(
|
||||||
|
cdc *codec.Codec, appGenTxs []json.RawMessage, tmVals []tmtypes.GenesisValidator, valOperAddrs []sdk.ValAddress,
|
||||||
|
) (GenesisState, error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(appGenTxs) == 0:
|
||||||
|
return GenesisState{}, errors.New("must provide at least genesis transaction")
|
||||||
|
case len(tmVals) != len(valOperAddrs):
|
||||||
|
return GenesisState{}, errors.New("number of TM validators does not match number of operator addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with the default staking genesis state
|
||||||
|
stakeData := stake.DefaultGenesisState()
|
||||||
|
|
||||||
|
// get genesis account information
|
||||||
|
genAccs := make([]GenesisAccount, len(appGenTxs))
|
||||||
|
for i, appGenTx := range appGenTxs {
|
||||||
|
|
||||||
|
var genTx GaiaGenTx
|
||||||
|
if err := cdc.UnmarshalJSON(appGenTx, &genTx); err != nil {
|
||||||
|
return GenesisState{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc))
|
||||||
|
|
||||||
|
// create the genesis account for the given genesis tx
|
||||||
|
genAccs[i] = genesisAccountFromGenTx(genTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tmVal := range tmVals {
|
||||||
|
var issuedDelShares sdk.Dec
|
||||||
|
|
||||||
|
// increase total supply by validator's power
|
||||||
|
power := sdk.NewInt(tmVal.Power)
|
||||||
|
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(power))
|
||||||
|
|
||||||
|
// add the validator
|
||||||
|
desc := stake.NewDescription(tmVal.Name, "", "", "")
|
||||||
|
validator := stake.NewValidator(valOperAddrs[i], tmVal.PubKey, desc)
|
||||||
|
|
||||||
|
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, power)
|
||||||
|
stakeData.Validators = append(stakeData.Validators, validator)
|
||||||
|
|
||||||
|
// create the self-delegation from the issuedDelShares
|
||||||
|
selfDel := stake.Delegation{
|
||||||
|
DelegatorAddr: sdk.AccAddress(validator.OperatorAddr),
|
||||||
|
ValidatorAddr: validator.OperatorAddr,
|
||||||
|
Shares: issuedDelShares,
|
||||||
|
Height: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
stakeData.Bonds = append(stakeData.Bonds, selfDel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenesisState{
|
||||||
|
Accounts: genAccs,
|
||||||
|
StakeData: stakeData,
|
||||||
|
GovData: gov.DefaultGenesisState(),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -138,11 +138,17 @@ func runPubKeyCmd(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consenusPub, err := sdk.Bech32ifyConsPub(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
fmt.Println("Address:", pubKey.Address())
|
fmt.Println("Address:", pubKey.Address())
|
||||||
fmt.Printf("Hex: %X\n", pubkeyBytes)
|
fmt.Printf("Hex: %X\n", pubkeyBytes)
|
||||||
fmt.Println("JSON (base64):", string(pubKeyJSONBytes))
|
fmt.Println("JSON (base64):", string(pubKeyJSONBytes))
|
||||||
fmt.Println("Bech32 Acc:", accPub)
|
fmt.Println("Bech32 Acc:", accPub)
|
||||||
fmt.Println("Bech32 Val:", valPub)
|
fmt.Println("Bech32 Validator Operator:", valPub)
|
||||||
|
fmt.Println("Bech32 Validator Consensus:", consenusPub)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
seeds=(1 2 4 7 9 20 32 123 4728 37827 981928 87821 891823782 989182 89182391)
|
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391)
|
||||||
|
blocks=$1
|
||||||
|
|
||||||
echo "Running multi-seed simulation with seeds: ${seeds[@]}"
|
echo "Running multi-seed simulation with seeds ${seeds[@]}"
|
||||||
|
echo "Running $blocks blocks per seed"
|
||||||
echo "Edit scripts/multisim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce."
|
echo "Edit scripts/multisim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce."
|
||||||
echo "This script will kill all sub-simulations on SIGINT/SIGTERM/EXIT (i.e. Ctrl-C)."
|
echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)."
|
||||||
|
|
||||||
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
|
trap 'kill $(jobs -pr)' SIGINT SIGTERM
|
||||||
|
|
||||||
tmpdir=$(mktemp -d)
|
tmpdir=$(mktemp -d)
|
||||||
echo "Using temporary log directory: $tmpdir"
|
echo "Using temporary log directory: $tmpdir"
|
||||||
|
@ -16,7 +18,7 @@ sim() {
|
||||||
echo "Running full Gaia simulation with seed $seed. This may take awhile!"
|
echo "Running full Gaia simulation with seed $seed. This may take awhile!"
|
||||||
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout"
|
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout"
|
||||||
echo "Writing stdout to $file..."
|
echo "Writing stdout to $file..."
|
||||||
go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 \
|
go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=$blocks \
|
||||||
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ for seed in ${seeds[@]}; do
|
||||||
sim $seed &
|
sim $seed &
|
||||||
pids[${i}]=$!
|
pids[${i}]=$!
|
||||||
i=$(($i+1))
|
i=$(($i+1))
|
||||||
sleep 0.1 # start in order, nicer logs
|
sleep 10 # start in order, nicer logs
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Simulation processes spawned, waiting for completion..."
|
echo "Simulation processes spawned, waiting for completion..."
|
||||||
|
@ -37,10 +39,13 @@ i=0
|
||||||
for pid in ${pids[*]}; do
|
for pid in ${pids[*]}; do
|
||||||
wait $pid
|
wait $pid
|
||||||
last=$?
|
last=$?
|
||||||
if [ $last -ne 0 ]; then
|
|
||||||
seed=${seeds[${i}]}
|
seed=${seeds[${i}]}
|
||||||
|
if [ $last -ne 0 ]
|
||||||
|
then
|
||||||
echo "Simulation with seed $seed failed!"
|
echo "Simulation with seed $seed failed!"
|
||||||
code=1
|
code=1
|
||||||
|
else
|
||||||
|
echo "Simulation with seed $seed OK"
|
||||||
fi
|
fi
|
||||||
i=$(($i+1))
|
i=$(($i+1))
|
||||||
done
|
done
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
// register REST routes
|
// register REST routes
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
"/accounts/{address}",
|
"/auth/accounts/{address}",
|
||||||
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
||||||
).Methods("GET")
|
).Methods("GET")
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
|
@ -36,7 +36,6 @@ func QueryAccountRequestHandlerFn(
|
||||||
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
||||||
) http.HandlerFunc {
|
) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bech32addr := vars["address"]
|
bech32addr := vars["address"]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package distribution
|
package distribution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -10,6 +12,7 @@ import (
|
||||||
// set the proposer for determining distribution during endblock
|
// set the proposer for determining distribution during endblock
|
||||||
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
|
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
|
||||||
consAddr := sdk.ConsAddress(req.Header.ProposerAddress)
|
consAddr := sdk.ConsAddress(req.Header.ProposerAddress)
|
||||||
|
fmt.Printf("debug consAddr: %v\n", consAddr)
|
||||||
k.SetProposerConsAddr(ctx, consAddr)
|
k.SetProposerConsAddr(ctx, consAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const (
|
||||||
numKeys int = 250
|
numKeys int = 250
|
||||||
|
|
||||||
// Chance that double-signing evidence is found on a given block
|
// Chance that double-signing evidence is found on a given block
|
||||||
evidenceFraction float64 = 0.01
|
evidenceFraction float64 = 0.5
|
||||||
|
|
||||||
// TODO Remove in favor of binary search for invariant violation
|
// TODO Remove in favor of binary search for invariant violation
|
||||||
onOperation bool = false
|
onOperation bool = false
|
||||||
|
|
|
@ -77,6 +77,9 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
||||||
}
|
}
|
||||||
|
|
||||||
validators := initChain(r, accs, setups, app, appStateFn)
|
validators := initChain(r, accs, setups, app, appStateFn)
|
||||||
|
// Second variable to keep pending validator set (delayed one block since TM 0.24)
|
||||||
|
// Initially this is the same as the initial validator set
|
||||||
|
nextValidators := validators
|
||||||
|
|
||||||
header := abci.Header{Height: 0, Time: timestamp}
|
header := abci.Header{Height: 0, Time: timestamp}
|
||||||
opCount := 0
|
opCount := 0
|
||||||
|
@ -160,8 +163,9 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
||||||
// Generate a random RequestBeginBlock with the current validator set for the next block
|
// Generate a random RequestBeginBlock with the current validator set for the next block
|
||||||
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
|
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
|
||||||
|
|
||||||
// Update the validator set
|
// Update the validator set, which will be reflected in the application on the next block
|
||||||
validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
|
validators = nextValidators
|
||||||
|
nextValidators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
|
||||||
}
|
}
|
||||||
if stopEarly {
|
if stopEarly {
|
||||||
DisplayEvents(events)
|
DisplayEvents(events)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
|
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
)
|
)
|
||||||
|
@ -56,19 +57,28 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
|
||||||
// Double sign confirmed
|
// Double sign confirmed
|
||||||
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge))
|
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge))
|
||||||
|
|
||||||
|
// We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height.
|
||||||
|
// Note that this *can* result in a negative "distributionHeight", up to -ValidatorUpdateDelay,
|
||||||
|
// i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block.
|
||||||
|
// That's fine since this is just used to filter unbonding delegations & redelegations.
|
||||||
|
distributionHeight := infractionHeight - stake.ValidatorUpdateDelay
|
||||||
|
|
||||||
// Cap the amount slashed to the penalty for the worst infraction
|
// Cap the amount slashed to the penalty for the worst infraction
|
||||||
// within the slashing period when this infraction was committed
|
// within the slashing period when this infraction was committed
|
||||||
fraction := k.SlashFractionDoubleSign(ctx)
|
fraction := k.SlashFractionDoubleSign(ctx)
|
||||||
revisedFraction := k.capBySlashingPeriod(ctx, consAddr, fraction, infractionHeight)
|
revisedFraction := k.capBySlashingPeriod(ctx, consAddr, fraction, distributionHeight)
|
||||||
logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction))
|
logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction))
|
||||||
|
|
||||||
// Slash validator
|
// Slash validator
|
||||||
k.validatorSet.Slash(ctx, consAddr, infractionHeight, power, revisedFraction)
|
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, revisedFraction)
|
||||||
|
|
||||||
// Jail validator
|
// Jail validator if not already jailed
|
||||||
|
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
|
||||||
|
if !validator.GetJailed() {
|
||||||
k.validatorSet.Jail(ctx, consAddr)
|
k.validatorSet.Jail(ctx, consAddr)
|
||||||
|
}
|
||||||
|
|
||||||
// Set validator jail duration
|
// Set or updated validator jail duration
|
||||||
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
|
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
|
||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
|
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
|
||||||
|
@ -123,7 +133,13 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
|
||||||
// Downtime confirmed: slash and jail the validator
|
// Downtime confirmed: slash and jail the validator
|
||||||
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d",
|
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d",
|
||||||
pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx)))
|
pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx)))
|
||||||
k.validatorSet.Slash(ctx, consAddr, height, power, k.SlashFractionDowntime(ctx))
|
// We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height,
|
||||||
|
// and subtract an additional 1 since this is the LastCommit.
|
||||||
|
// Note that this *can* result in a negative "distributionHeight" up to -ValidatorUpdateDelay-1,
|
||||||
|
// i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block.
|
||||||
|
// That's fine since this is just used to filter unbonding delegations & redelegations.
|
||||||
|
distributionHeight := height - stake.ValidatorUpdateDelay - 1
|
||||||
|
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx))
|
||||||
k.validatorSet.Jail(ctx, consAddr)
|
k.validatorSet.Jail(ctx, consAddr)
|
||||||
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
|
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,20 +22,21 @@ func init() {
|
||||||
|
|
||||||
// Test that a validator is slashed correctly
|
// Test that a validator is slashed correctly
|
||||||
// when we discover evidence of infraction
|
// when we discover evidence of infraction
|
||||||
// TODO fix this test to not be using the same pubkey/address for signing and operating, it's confusing
|
|
||||||
func TestHandleDoubleSign(t *testing.T) {
|
func TestHandleDoubleSign(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
|
// validator added pre-genesis
|
||||||
|
ctx = ctx.WithBlockHeight(-1)
|
||||||
sk = sk.WithHooks(keeper.Hooks())
|
sk = sk.WithHooks(keeper.Hooks())
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, val, amt))
|
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(operatorAddr, val, amt))
|
||||||
require.True(t, got.IsOK())
|
require.True(t, got.IsOK())
|
||||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||||
keeper.AddValidators(ctx, validatorUpdates)
|
keeper.AddValidators(ctx, validatorUpdates)
|
||||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
|
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower()))
|
||||||
|
|
||||||
// handle a signature to set signing info
|
// handle a signature to set signing info
|
||||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
|
||||||
|
@ -44,13 +45,13 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||||
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
|
||||||
|
|
||||||
// should be jailed
|
// should be jailed
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, operatorAddr).GetJailed())
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, sdk.ConsAddress(addr)) // TODO distinguish cons address
|
sk.Unjail(ctx, sdk.ConsAddress(val.Address()))
|
||||||
// power should be reduced
|
// power should be reduced
|
||||||
require.Equal(
|
require.Equal(
|
||||||
t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))),
|
t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))),
|
||||||
sk.Validator(ctx, addr).GetPower(),
|
sk.Validator(ctx, operatorAddr).GetPower(),
|
||||||
)
|
)
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))})
|
||||||
|
|
||||||
|
@ -58,74 +59,74 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||||
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
|
||||||
require.Equal(
|
require.Equal(
|
||||||
t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))),
|
t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))),
|
||||||
sk.Validator(ctx, addr).GetPower(),
|
sk.Validator(ctx, operatorAddr).GetPower(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the amount a validator is slashed for multiple double signs
|
// Test that the amount a validator is slashed for multiple double signs
|
||||||
// is correctly capped by the slashing period in which they were committed
|
// is correctly capped by the slashing period in which they were committed
|
||||||
// TODO properly distinguish between consensus and operator address is variable names
|
|
||||||
func TestSlashingPeriodCap(t *testing.T) {
|
func TestSlashingPeriodCap(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
sk = sk.WithHooks(keeper.Hooks())
|
sk = sk.WithHooks(keeper.Hooks())
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, amt := addrs[0], sdk.NewInt(amtInt)
|
operatorAddr, amt := addrs[0], sdk.NewInt(amtInt)
|
||||||
valConsPubKey, valConsAddr := pks[0], sdk.ConsAddress(pks[0].Address())
|
valConsPubKey, valConsAddr := pks[0], pks[0].Address()
|
||||||
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, valConsPubKey, amt))
|
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(operatorAddr, valConsPubKey, amt))
|
||||||
require.True(t, got.IsOK())
|
require.True(t, got.IsOK())
|
||||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||||
|
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
|
||||||
keeper.AddValidators(ctx, validatorUpdates)
|
keeper.AddValidators(ctx, validatorUpdates)
|
||||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
|
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower()))
|
||||||
|
|
||||||
// handle a signature to set signing info
|
// handle a signature to set signing info
|
||||||
keeper.handleValidatorSignature(ctx, valConsPubKey.Address(), amtInt, true)
|
keeper.handleValidatorSignature(ctx, valConsAddr, amtInt, true)
|
||||||
|
|
||||||
// double sign less than max age
|
// double sign less than max age
|
||||||
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, valConsAddr, 1, time.Unix(0, 0), amtInt)
|
||||||
// should be jailed
|
// should be jailed
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, operatorAddr).GetJailed())
|
||||||
// end block
|
|
||||||
stake.EndBlocker(ctx, sk)
|
|
||||||
// update block height
|
|
||||||
ctx = ctx.WithBlockHeight(int64(1))
|
|
||||||
// unjail to measure power
|
|
||||||
sk.Unjail(ctx, valConsAddr)
|
|
||||||
// end block
|
|
||||||
stake.EndBlocker(ctx, sk)
|
|
||||||
// power should be reduced
|
|
||||||
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
|
||||||
|
|
||||||
// double sign again, same slashing period
|
|
||||||
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
|
||||||
// should be jailed
|
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
|
||||||
// end block
|
// end block
|
||||||
stake.EndBlocker(ctx, sk)
|
stake.EndBlocker(ctx, sk)
|
||||||
// update block height
|
// update block height
|
||||||
ctx = ctx.WithBlockHeight(int64(2))
|
ctx = ctx.WithBlockHeight(int64(2))
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, valConsAddr)
|
sk.Unjail(ctx, sdk.ConsAddress(valConsAddr))
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
// power should be reduced
|
||||||
|
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
||||||
|
require.Equal(t, expectedPower, sk.Validator(ctx, operatorAddr).GetPower())
|
||||||
|
|
||||||
|
// double sign again, same slashing period
|
||||||
|
keeper.handleDoubleSign(ctx, valConsAddr, 1, time.Unix(0, 0), amtInt)
|
||||||
|
// should be jailed
|
||||||
|
require.True(t, sk.Validator(ctx, operatorAddr).GetJailed())
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
// update block height
|
||||||
|
ctx = ctx.WithBlockHeight(int64(3))
|
||||||
|
// unjail to measure power
|
||||||
|
sk.Unjail(ctx, sdk.ConsAddress(valConsAddr))
|
||||||
// end block
|
// end block
|
||||||
stake.EndBlocker(ctx, sk)
|
stake.EndBlocker(ctx, sk)
|
||||||
// power should be equal, no more should have been slashed
|
// power should be equal, no more should have been slashed
|
||||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, expectedPower, sk.Validator(ctx, operatorAddr).GetPower())
|
||||||
|
|
||||||
// double sign again, new slashing period
|
// double sign again, new slashing period
|
||||||
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 2, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, valConsAddr, 3, time.Unix(0, 0), amtInt)
|
||||||
// should be jailed
|
// should be jailed
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, operatorAddr).GetJailed())
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, valConsAddr)
|
sk.Unjail(ctx, sdk.ConsAddress(valConsAddr))
|
||||||
// end block
|
// end block
|
||||||
stake.EndBlocker(ctx, sk)
|
stake.EndBlocker(ctx, sk)
|
||||||
// power should be reduced
|
// power should be reduced
|
||||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
|
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, expectedPower, sk.Validator(ctx, operatorAddr).GetPower())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test a validator through uptime, downtime, revocation,
|
// Test a validator through uptime, downtime, revocation,
|
||||||
|
@ -196,6 +197,35 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||||
|
|
||||||
|
slashAmt := sdk.NewDec(amtInt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64()
|
||||||
|
|
||||||
|
// validator should have been slashed
|
||||||
|
require.Equal(t, amtInt-slashAmt, validator.GetTokens().RoundInt64())
|
||||||
|
|
||||||
|
// 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator)
|
||||||
|
height++
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||||
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
|
// validator should not have been slashed any more, since it was already jailed
|
||||||
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
|
require.Equal(t, amtInt-slashAmt, validator.GetTokens().RoundInt64())
|
||||||
|
|
||||||
|
// 502nd block *double signed* (oh no!)
|
||||||
|
keeper.handleDoubleSign(ctx, val.Address(), height, ctx.BlockHeader().Time, amtInt)
|
||||||
|
|
||||||
|
// validator should have been slashed
|
||||||
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
|
secondSlashAmt := sdk.NewDec(amtInt).Mul(keeper.SlashFractionDoubleSign(ctx)).RoundInt64()
|
||||||
|
require.Equal(t, amtInt-slashAmt-secondSlashAmt, validator.GetTokens().RoundInt64())
|
||||||
|
|
||||||
// unrevocation should fail prior to jail expiration
|
// unrevocation should fail prior to jail expiration
|
||||||
got = slh(ctx, NewMsgUnjail(addr))
|
got = slh(ctx, NewMsgUnjail(addr))
|
||||||
require.False(t, got.IsOK())
|
require.False(t, got.IsOK())
|
||||||
|
@ -214,14 +244,14 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
|
|
||||||
// validator should have been slashed
|
// validator should have been slashed
|
||||||
pool = sk.GetPool(ctx)
|
pool = sk.GetPool(ctx)
|
||||||
slashAmt := sdk.NewDec(amtInt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64()
|
require.Equal(t, amtInt-slashAmt-secondSlashAmt, pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, amtInt-slashAmt, pool.BondedTokens.RoundInt64())
|
|
||||||
|
|
||||||
// validator start height should have been changed
|
// validator start height should have been changed
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, height, info.StartHeight)
|
require.Equal(t, height, info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
// we've missed 2 blocks more than the maximum
|
||||||
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
|
||||||
|
|
||||||
// validator should not be immediately jailed again
|
// validator should not be immediately jailed again
|
||||||
height++
|
height++
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// key prefix bytes
|
// key prefix bytes
|
||||||
|
@ -34,7 +35,8 @@ func GetValidatorSlashingPeriodPrefix(v sdk.ConsAddress) []byte {
|
||||||
// stored by *Tendermint* address (not operator address) followed by start height
|
// stored by *Tendermint* address (not operator address) followed by start height
|
||||||
func GetValidatorSlashingPeriodKey(v sdk.ConsAddress, startHeight int64) []byte {
|
func GetValidatorSlashingPeriodKey(v sdk.ConsAddress, startHeight int64) []byte {
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(b, uint64(startHeight))
|
// this needs to be height + ValidatorUpdateDelay because the slashing period for genesis validators starts at height -ValidatorUpdateDelay
|
||||||
|
binary.BigEndian.PutUint64(b, uint64(startHeight+stake.ValidatorUpdateDelay))
|
||||||
return append(GetValidatorSlashingPeriodPrefix(v), b...)
|
return append(GetValidatorSlashingPeriodPrefix(v), b...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cap an infraction's slash amount by the slashing period in which it was committed
|
// Cap an infraction's slash amount by the slashing period in which it was committed
|
||||||
|
@ -15,7 +16,7 @@ func (k Keeper) capBySlashingPeriod(ctx sdk.Context, address sdk.ConsAddress, fr
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if slashingPeriod.EndHeight > 0 && slashingPeriod.EndHeight < infractionHeight {
|
if slashingPeriod.EndHeight > 0 && slashingPeriod.EndHeight < infractionHeight {
|
||||||
panic(fmt.Sprintf("slashing period ended before infraction: infraction height %d, slashing period ended at %d", infractionHeight, slashingPeriod.EndHeight))
|
panic(fmt.Sprintf("slashing period ended before infraction: validator %s, infraction height %d, slashing period ended at %d", address, infractionHeight, slashingPeriod.EndHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the updated total slash amount
|
// Calculate the updated total slash amount
|
||||||
|
@ -43,7 +44,7 @@ func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk
|
||||||
end := sdk.PrefixEndBytes(GetValidatorSlashingPeriodKey(address, height))
|
end := sdk.PrefixEndBytes(GetValidatorSlashingPeriodKey(address, height))
|
||||||
iterator := store.ReverseIterator(start, end)
|
iterator := store.ReverseIterator(start, end)
|
||||||
if !iterator.Valid() {
|
if !iterator.Valid() {
|
||||||
panic("expected to find slashing period, but none was found")
|
panic(fmt.Sprintf("expected to find slashing period for validator %s before height %d, but none was found", address, height))
|
||||||
}
|
}
|
||||||
slashingPeriod = k.unmarshalSlashingPeriodKeyValue(iterator.Key(), iterator.Value())
|
slashingPeriod = k.unmarshalSlashingPeriodKeyValue(iterator.Key(), iterator.Value())
|
||||||
return
|
return
|
||||||
|
@ -68,7 +69,7 @@ func (k Keeper) unmarshalSlashingPeriodKeyValue(key []byte, value []byte) Valida
|
||||||
var slashingPeriodValue ValidatorSlashingPeriodValue
|
var slashingPeriodValue ValidatorSlashingPeriodValue
|
||||||
k.cdc.MustUnmarshalBinary(value, &slashingPeriodValue)
|
k.cdc.MustUnmarshalBinary(value, &slashingPeriodValue)
|
||||||
address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen])
|
address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen])
|
||||||
startHeight := int64(binary.LittleEndian.Uint64(key[1+sdk.AddrLen : 1+sdk.AddrLen+8]))
|
startHeight := int64(binary.BigEndian.Uint64(key[1+sdk.AddrLen:1+sdk.AddrLen+8]) - uint64(stake.ValidatorUpdateDelay))
|
||||||
return ValidatorSlashingPeriod{
|
return ValidatorSlashingPeriod{
|
||||||
ValidatorAddr: address,
|
ValidatorAddr: address,
|
||||||
StartHeight: startHeight,
|
StartHeight: startHeight,
|
||||||
|
|
|
@ -18,6 +18,12 @@ import (
|
||||||
// the bonded validators.
|
// the bonded validators.
|
||||||
// Returns final validator set after applying all declaration and delegations
|
// Returns final validator set after applying all declaration and delegations
|
||||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
|
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
|
||||||
|
|
||||||
|
// We need to pretend to be "n blocks before genesis", where "n" is the validator update delay,
|
||||||
|
// so that e.g. slashing periods are correctly initialized for the validator set
|
||||||
|
// e.g. with a one-block offset - the first TM block is at height 0, so state updates applied from genesis.json are in block -1.
|
||||||
|
ctx = ctx.WithBlockHeight(-types.ValidatorUpdateDelay)
|
||||||
|
|
||||||
keeper.SetPool(ctx, data.Pool)
|
keeper.SetPool(ctx, data.Pool)
|
||||||
keeper.SetParams(ctx, data.Params)
|
keeper.SetParams(ctx, data.Params)
|
||||||
keeper.InitIntraTxCounter(ctx)
|
keeper.InitIntraTxCounter(ctx)
|
||||||
|
|
|
@ -191,6 +191,34 @@ func TestSlashAtFutureHeight(t *testing.T) {
|
||||||
require.Panics(t, func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) })
|
require.Panics(t, func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test slash at a negative height
|
||||||
|
// this just represents pre-genesis and should have the same effect as slashing at height 0
|
||||||
|
func TestSlashAtNegativeHeight(t *testing.T) {
|
||||||
|
ctx, keeper, _ := setupHelper(t, 10)
|
||||||
|
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||||
|
fraction := sdk.NewDecWithPrec(5, 1)
|
||||||
|
|
||||||
|
oldPool := keeper.GetPool(ctx)
|
||||||
|
validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
keeper.Slash(ctx, consAddr, -2, 10, fraction)
|
||||||
|
|
||||||
|
// read updated state
|
||||||
|
validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
newPool := keeper.GetPool(ctx)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates)
|
||||||
|
|
||||||
|
validator = keeper.mustGetValidator(ctx, validator.OperatorAddr)
|
||||||
|
// power decreased
|
||||||
|
require.Equal(t, sdk.NewDec(5), validator.GetPower())
|
||||||
|
// pool bonded shares decreased
|
||||||
|
require.Equal(t, sdk.NewDec(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
|
}
|
||||||
|
|
||||||
// tests Slash at the current height
|
// tests Slash at the current height
|
||||||
func TestSlashValidatorAtCurrentHeight(t *testing.T) {
|
func TestSlashValidatorAtCurrentHeight(t *testing.T) {
|
||||||
ctx, keeper, _ := setupHelper(t, 10)
|
ctx, keeper, _ := setupHelper(t, 10)
|
||||||
|
|
|
@ -9,9 +9,17 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
// defaultUnbondingTime reflects three weeks in seconds as the default
|
// defaultUnbondingTime reflects three weeks in seconds as the default
|
||||||
// unbonding time.
|
// unbonding time.
|
||||||
const defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second
|
defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second
|
||||||
|
|
||||||
|
// Delay, in blocks, between when validator updates are returned to Tendermint and when they are applied
|
||||||
|
// For example, if this is 0, the validator set at the end of a block will sign the next block, or
|
||||||
|
// if this is 1, the validator set at the end of a block will sign the block after the next.
|
||||||
|
// Constant as this should not change without a hard fork.
|
||||||
|
ValidatorUpdateDelay int64 = 1
|
||||||
|
)
|
||||||
|
|
||||||
// Params defines the high level settings for staking
|
// Params defines the high level settings for staking
|
||||||
type Params struct {
|
type Params struct {
|
||||||
|
|
Loading…
Reference in New Issue