Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution
This commit is contained in:
commit
d7794b483d
|
@ -34,7 +34,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79"
|
||||
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = "UT"
|
||||
|
@ -71,7 +71,7 @@
|
|||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe"
|
||||
digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
|
||||
name = "github.com/go-kit/kit"
|
||||
packages = [
|
||||
"log",
|
||||
|
@ -103,7 +103,7 @@
|
|||
version = "v1.7.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45"
|
||||
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"gogoproto",
|
||||
|
@ -118,7 +118,7 @@
|
|||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888"
|
||||
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
|
@ -165,13 +165,12 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ac64f01acc5eeea9dde40e326de6b6471e501392ec06524c3b51033aa50789bc"
|
||||
digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
|
@ -263,7 +262,7 @@
|
|||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e"
|
||||
digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
|
@ -274,7 +273,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a"
|
||||
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
pruneopts = "UT"
|
||||
|
@ -282,7 +281,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3"
|
||||
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
|
@ -294,7 +293,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd"
|
||||
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -313,7 +312,7 @@
|
|||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c"
|
||||
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -332,7 +331,7 @@
|
|||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26"
|
||||
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
|
@ -364,7 +363,7 @@
|
|||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d"
|
||||
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
|
@ -376,7 +375,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:442d2ffa75ffae302ce8800bf4144696b92bef02917923ea132ce2d39efe7d65"
|
||||
digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
packages = [
|
||||
"leveldb",
|
||||
|
@ -397,7 +396,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232"
|
||||
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
|
||||
name = "github.com/tendermint/ed25519"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -424,7 +423,7 @@
|
|||
version = "v0.9.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:963f6c04345ce36f900c1d6367200eebc3cc2db6ee632ff865ea8dcf64b748a0"
|
||||
digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6"
|
||||
name = "github.com/tendermint/tendermint"
|
||||
packages = [
|
||||
"abci/client",
|
||||
|
@ -491,7 +490,7 @@
|
|||
version = "v0.23.1-rc0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ad879bb8c71020a3f92f0c61f414d93eae1d5dc2f37023b6abaa3cc84b00165e"
|
||||
digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e"
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
packages = ["cli"]
|
||||
pruneopts = "UT"
|
||||
|
@ -507,7 +506,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2a3ce1f08dcae8bac666deb6e4c88b5d7170c510da38fd746231144cac351704"
|
||||
digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"blowfish",
|
||||
|
@ -529,7 +528,7 @@
|
|||
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9"
|
||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
|
@ -546,7 +545,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c8baf78f0ac6eb27c645e264fe5e8a74d5a50db188ab41a7ff3b275c112e0735"
|
||||
digest = "1:86171d21d59449dcf7cee0b7d2da83dff989dab9b9b69bfe0a3d59c3c1ca6081"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
|
@ -556,7 +555,7 @@
|
|||
revision = "11551d06cbcc94edc80a0facaccbda56473c19c1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca"
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
|
@ -587,7 +586,7 @@
|
|||
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac"
|
||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -664,6 +663,8 @@
|
|||
"github.com/tendermint/tendermint/libs/common",
|
||||
"github.com/tendermint/tendermint/libs/db",
|
||||
"github.com/tendermint/tendermint/libs/log",
|
||||
"github.com/tendermint/tendermint/lite",
|
||||
"github.com/tendermint/tendermint/lite/proxy",
|
||||
"github.com/tendermint/tendermint/node",
|
||||
"github.com/tendermint/tendermint/p2p",
|
||||
"github.com/tendermint/tendermint/privval",
|
||||
|
|
11
Makefile
11
Makefile
|
@ -163,6 +163,17 @@ test_sim_gaia_slow:
|
|||
@echo "Running full Gaia simulation. This may take awhile!"
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -v -timeout 24h
|
||||
|
||||
SIM_NUM_BLOCKS ?= 210
|
||||
SIM_BLOCK_SIZE ?= 200
|
||||
SIM_COMMIT ?= true
|
||||
test_sim_gaia_benchmark:
|
||||
@echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!"
|
||||
@go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h
|
||||
|
||||
test_sim_gaia_profile:
|
||||
@echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!"
|
||||
@go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ BREAKING CHANGES
|
|||
* [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface.
|
||||
* [docs] [#2001](https://github.com/cosmos/cosmos-sdk/pull/2001) Update slashing spec for slashing period
|
||||
* [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] [#1877] Redelegations/unbonding-delegation from unbonding validator have reduced time
|
||||
* [x/stake] \#2040 Validator operator type has now changed to `sdk.ValAddress`
|
||||
* A new bech32 prefix has been introduced for Tendermint signing keys and
|
||||
addresses, `cosmosconspub` and `cosmoscons` respectively.
|
||||
|
@ -30,6 +32,7 @@ BREAKING CHANGES
|
|||
* SDK
|
||||
* [core] \#1807 Switch from use of rational to decimal
|
||||
* [types] \#1901 Validator interface's GetOwner() renamed to GetOperator()
|
||||
* [x/slashing] [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122) - Implement slashing period
|
||||
* [types] \#2119 Parsed error messages and ABCI log errors to make them more human readable.
|
||||
* [simulation] Rename TestAndRunTx to Operation [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153)
|
||||
|
||||
|
@ -40,6 +43,7 @@ FEATURES
|
|||
|
||||
* Gaia REST API (`gaiacli advanced rest-server`)
|
||||
* [lcd] Endpoints to query staking pool and params
|
||||
* [lcd] \#2110 Add support for `simulate=true` requests query argument to endpoints that send txs to run simulations of transactions
|
||||
|
||||
* Gaia CLI (`gaiacli`)
|
||||
* [cli] Cmds to query staking pool and params
|
||||
|
@ -48,6 +52,7 @@ FEATURES
|
|||
provide desired Bech32 prefix encoding
|
||||
* [cli] \#2047 Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution.
|
||||
* [cli] \#2047 The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0.
|
||||
* [cli] \#2110 Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated.
|
||||
|
||||
* Gaia
|
||||
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
|
||||
|
@ -55,6 +60,7 @@ FEATURES
|
|||
* SDK
|
||||
* [querier] added custom querier functionality, so ABCI query requests can be handled by keepers
|
||||
* [simulation] \#1924 allow operations to specify future operations
|
||||
* [simulation] \#1924 Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile"
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
@ -73,7 +79,8 @@ IMPROVEMENTS
|
|||
* Gaia
|
||||
* [x/stake] [#2023](https://github.com/cosmos/cosmos-sdk/pull/2023) Terminate iteration loop in `UpdateBondedValidators` and `UpdateBondedValidatorsFull` when the first revoked validator is encountered and perform a sanity check.
|
||||
* [x/auth] Signature verification's gas cost now accounts for pubkey type. [#2046](https://github.com/tendermint/tendermint/pull/2046)
|
||||
|
||||
* [x/stake] [x/slashing] Ensure delegation invariants to jailed validators [#1883](https://github.com/cosmos/cosmos-sdk/issues/1883).
|
||||
* [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200)
|
||||
* SDK
|
||||
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
|
||||
* [spec] Added simple piggy bank distribution spec
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
tmlite "github.com/tendermint/tendermint/lite"
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
)
|
||||
|
||||
|
@ -32,6 +36,8 @@ type CLIContext struct {
|
|||
Async bool
|
||||
JSON bool
|
||||
PrintResponse bool
|
||||
Certifier tmlite.Certifier
|
||||
DryRun bool
|
||||
}
|
||||
|
||||
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
||||
|
@ -57,9 +63,41 @@ func NewCLIContext() CLIContext {
|
|||
Async: viper.GetBool(client.FlagAsync),
|
||||
JSON: viper.GetBool(client.FlagJson),
|
||||
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
||||
Certifier: createCertifier(),
|
||||
DryRun: viper.GetBool(client.FlagDryRun),
|
||||
}
|
||||
}
|
||||
|
||||
func createCertifier() tmlite.Certifier {
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
if trustNode {
|
||||
return nil
|
||||
}
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
home := viper.GetString(cli.HomeFlag)
|
||||
nodeURI := viper.GetString(client.FlagNode)
|
||||
|
||||
var errMsg bytes.Buffer
|
||||
if chainID == "" {
|
||||
errMsg.WriteString("chain-id ")
|
||||
}
|
||||
if home == "" {
|
||||
errMsg.WriteString("home ")
|
||||
}
|
||||
if nodeURI == "" {
|
||||
errMsg.WriteString("node ")
|
||||
}
|
||||
// errMsg is not empty
|
||||
if errMsg.Len() != 0 {
|
||||
panic(fmt.Errorf("can't create certifier for distrust mode, empty values from these options: %s", errMsg.String()))
|
||||
}
|
||||
certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return certifier
|
||||
}
|
||||
|
||||
// WithCodec returns a copy of the context with an updated codec.
|
||||
func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext {
|
||||
ctx.Codec = cdc
|
||||
|
@ -117,3 +155,15 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
|
|||
ctx.UseLedger = useLedger
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithCertifier - return a copy of the context with an updated Certifier
|
||||
func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext {
|
||||
ctx.Certifier = certifier
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithGasAdjustment returns a copy of the context with an updated GasAdjustment flag.
|
||||
func (ctx CLIContext) WithGasAdjustment(adjustment float64) CLIContext {
|
||||
ctx.GasAdjustment = adjustment
|
||||
return ctx
|
||||
}
|
||||
|
|
|
@ -10,9 +10,14 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetNode returns an RPC client. If the context's client is not defined, an
|
||||
|
@ -304,12 +309,77 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
|
|||
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
|
||||
}
|
||||
|
||||
// Data from trusted node or subspace query doesn't need verification
|
||||
if ctx.TrustNode || !isQueryStoreWithProof(path) {
|
||||
return resp.Value, nil
|
||||
}
|
||||
|
||||
err = ctx.verifyProof(path, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Value, nil
|
||||
}
|
||||
|
||||
// verifyProof perform response proof verification
|
||||
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
|
||||
|
||||
if ctx.Certifier == nil {
|
||||
return fmt.Errorf("missing valid certifier to verify data from untrusted node")
|
||||
}
|
||||
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// AppHash for height H is in header H+1
|
||||
commit, err := tmliteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var multiStoreProof store.MultiStoreProof
|
||||
cdc := wire.NewCodec()
|
||||
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshalBinary rangeProof")
|
||||
}
|
||||
|
||||
// Verify the substore commit hash against trusted appHash
|
||||
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
|
||||
multiStoreProof.StoreInfos, commit.Header.AppHash)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in verifying the proof against appHash")
|
||||
}
|
||||
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in the range proof verification")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryStore performs a query from a Tendermint node with the provided a store
|
||||
// name and path.
|
||||
func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) {
|
||||
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
|
||||
return ctx.query(path, key)
|
||||
}
|
||||
|
||||
// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
|
||||
// queryType can be app or store
|
||||
func isQueryStoreWithProof(path string) bool {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return false
|
||||
}
|
||||
paths := strings.SplitN(path[1:], "/", 3)
|
||||
if len(paths) != 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
if store.RequireProof("/" + paths[2]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ import "github.com/spf13/cobra"
|
|||
|
||||
// nolint
|
||||
const (
|
||||
// DefaultGasAdjustment is applied to gas estimates to avoid tx
|
||||
// execution failures due to state changes that might
|
||||
// occur between the tx simulation and the actual run.
|
||||
DefaultGasAdjustment = 1.0
|
||||
DefaultGasLimit = 200000
|
||||
DefaultGasAdjustment = 1.2
|
||||
|
||||
FlagUseLedger = "ledger"
|
||||
FlagChainID = "chain-id"
|
||||
|
@ -23,6 +26,7 @@ const (
|
|||
FlagAsync = "async"
|
||||
FlagJson = "json"
|
||||
FlagPrintResponse = "print-response"
|
||||
FlagDryRun = "dry-run"
|
||||
)
|
||||
|
||||
// LineBreak can be included in a command list to provide a blank line
|
||||
|
@ -54,10 +58,12 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
|||
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
||||
c.Flags().Int64(FlagGas, DefaultGasLimit, "gas limit to set per-transaction; set to 0 to calculate required gas automatically")
|
||||
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation")
|
||||
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
|
||||
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
|
||||
c.Flags().Bool(FlagJson, false, "return output in json format")
|
||||
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
|
||||
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses")
|
||||
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package lcd
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
@ -265,11 +266,21 @@ func TestCoinSend(t *testing.T) {
|
|||
require.Equal(t, int64(1), mycoins.Amount.Int64())
|
||||
|
||||
// test failure with too little gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100)
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test success with just enough gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 3000)
|
||||
// test failure with wrong adjustment
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// run simulation and test success with estimated gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?simulate=true")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var responseBody struct {
|
||||
GasEstimate int64 `json:"gas_estimate"`
|
||||
}
|
||||
require.Nil(t, json.Unmarshal([]byte(body), &responseBody))
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
}
|
||||
|
||||
|
@ -720,7 +731,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
|||
return acc
|
||||
}
|
||||
|
||||
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
|
@ -744,22 +755,28 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
|||
"gas":"%v",
|
||||
`, gas)
|
||||
}
|
||||
gasAdjustmentStr := ""
|
||||
if gasAdjustment > 0 {
|
||||
gasStr = fmt.Sprintf(`
|
||||
"gas_adjustment":"%v",
|
||||
`, gasAdjustment)
|
||||
}
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
%v
|
||||
%v%v
|
||||
"name":"%s",
|
||||
"password":"%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d",
|
||||
"amount":[%s],
|
||||
"chain_id":"%s"
|
||||
}`, gasStr, name, password, accnum, sequence, coinbz, chainID))
|
||||
}`, gasStr, gasAdjustmentStr, name, password, accnum, sequence, coinbz, chainID))
|
||||
|
||||
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr)
|
||||
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr)
|
||||
return
|
||||
}
|
||||
|
||||
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, 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
tx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
||||
|
@ -66,6 +66,7 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
|||
cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to")
|
||||
cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to")
|
||||
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -190,6 +190,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
|||
node, err := startTM(config, logger, genDoc, privVal, app)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress))
|
||||
lcd, err := startLCD(logger, listenAddr, cdc)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -1,12 +1,45 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
queryArgDryRun = "simulate"
|
||||
)
|
||||
|
||||
// WriteErrorResponse prepares and writes a HTTP error
|
||||
// given a status code and an error message.
|
||||
func WriteErrorResponse(w *http.ResponseWriter, status int, msg string) {
|
||||
(*w).WriteHeader(status)
|
||||
(*w).Write([]byte(msg))
|
||||
func WriteErrorResponse(w http.ResponseWriter, status int, msg string) {
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(msg))
|
||||
}
|
||||
|
||||
// WriteGasEstimateResponse prepares and writes an HTTP
|
||||
// response for transactions simulations.
|
||||
func WriteSimulationResponse(w http.ResponseWriter, gas int64) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas)))
|
||||
}
|
||||
|
||||
// HasDryRunArg returns true if the request's URL query contains
|
||||
// the dry run argument and its value is set to "true".
|
||||
func HasDryRunArg(r *http.Request) bool {
|
||||
return r.URL.Query().Get(queryArgDryRun) == "true"
|
||||
}
|
||||
|
||||
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a default
|
||||
// value if the string is empty. Write
|
||||
func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) {
|
||||
if len(s) == 0 {
|
||||
return defaultIfEmpty, true
|
||||
}
|
||||
n, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return n, false
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
|
|
@ -12,46 +12,26 @@ import (
|
|||
"github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// DefaultGasAdjustment is applied to gas estimates to avoid tx
|
||||
// execution failures due to state changes that might
|
||||
// occur between the tx simulation and the actual run.
|
||||
const DefaultGasAdjustment = 1.2
|
||||
|
||||
// SendTx implements a auxiliary handler that facilitates sending a series of
|
||||
// messages in a signed transaction given a TxContext and a QueryContext. It
|
||||
// ensures that the account exists, has a proper number and sequence set. In
|
||||
// addition, it builds and signs a transaction with the supplied messages.
|
||||
// Finally, it broadcasts the signed transaction to a node.
|
||||
func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) error {
|
||||
if err := cliCtx.EnsureAccountExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
from, err := cliCtx.GetFromAddress()
|
||||
txCtx, err := prepareTxContext(txCtx, cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account number without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.AccountNumber == 0 {
|
||||
accNum, err := cliCtx.GetAccountNumber(from)
|
||||
autogas := cliCtx.DryRun || (cliCtx.Gas == 0)
|
||||
if autogas {
|
||||
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txCtx = txCtx.WithAccountNumber(accNum)
|
||||
fmt.Fprintf(os.Stdout, "estimated gas = %v\n", txCtx.Gas)
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account sequence without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.Sequence == 0 {
|
||||
accSeq, err := cliCtx.GetAccountSequence(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txCtx = txCtx.WithSequence(accSeq)
|
||||
if cliCtx.DryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
|
||||
|
@ -59,13 +39,6 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
|
|||
return err
|
||||
}
|
||||
|
||||
if cliCtx.Gas == 0 {
|
||||
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// build and sign the transaction
|
||||
txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
|
||||
if err != nil {
|
||||
|
@ -75,24 +48,24 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
|
|||
return cliCtx.EnsureBroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
|
||||
// transaction and set the transaction's respective value accordingly.
|
||||
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) {
|
||||
txBytes, err := BuildAndSignTxWithZeroGas(txCtx, name, passphrase, msgs)
|
||||
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
|
||||
func SimulateMsgs(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, msgs []sdk.Msg, gas int64) (estimated, adjusted int64, err error) {
|
||||
txBytes, err := txCtx.WithGas(gas).BuildWithPubKey(name, msgs)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
return
|
||||
}
|
||||
estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted)
|
||||
return txCtx.WithGas(adjusted), nil
|
||||
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
|
||||
return
|
||||
}
|
||||
|
||||
// BuildAndSignTxWithZeroGas builds transactions with GasWanted set to 0.
|
||||
func BuildAndSignTxWithZeroGas(txCtx authctx.TxContext, name, passphrase string, msgs []sdk.Msg) ([]byte, error) {
|
||||
return txCtx.WithGas(0).BuildAndSign(name, passphrase, msgs)
|
||||
// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
|
||||
// transaction and set the transaction's respective value accordingly.
|
||||
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authctx.TxContext, error) {
|
||||
_, adjusted, err := SimulateMsgs(txCtx, cliCtx, name, msgs, 0)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
return txCtx.WithGas(adjusted), nil
|
||||
}
|
||||
|
||||
// CalculateGas simulates the execution of a transaction and returns
|
||||
|
@ -109,14 +82,10 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
|
|||
return
|
||||
}
|
||||
adjusted = adjustGasEstimate(estimate, adjustment)
|
||||
fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted)
|
||||
return
|
||||
}
|
||||
|
||||
func adjustGasEstimate(estimate int64, adjustment float64) int64 {
|
||||
if adjustment == 0 {
|
||||
return int64(DefaultGasAdjustment * float64(estimate))
|
||||
}
|
||||
return int64(adjustment * float64(estimate))
|
||||
}
|
||||
|
||||
|
@ -127,3 +96,35 @@ func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
|
|||
}
|
||||
return simulationResult.GasUsed, nil
|
||||
}
|
||||
|
||||
func prepareTxContext(txCtx authctx.TxContext, cliCtx context.CLIContext) (authctx.TxContext, error) {
|
||||
if err := cliCtx.EnsureAccountExists(); err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
|
||||
from, err := cliCtx.GetFromAddress()
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account number without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.AccountNumber == 0 {
|
||||
accNum, err := cliCtx.GetAccountNumber(from)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
txCtx = txCtx.WithAccountNumber(accNum)
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account sequence without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.Sequence == 0 {
|
||||
accSeq, err := cliCtx.GetAccountSequence(from)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
}
|
||||
txCtx = txCtx.WithSequence(accSeq)
|
||||
}
|
||||
return txCtx, nil
|
||||
}
|
||||
|
|
|
@ -93,9 +93,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
|||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||
app.stakeKeeper = app.stakeKeeper.WithValidatorHooks(app.slashingKeeper.ValidatorHooks())
|
||||
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
|
||||
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||
|
||||
// register message routes
|
||||
app.Router().
|
||||
|
|
|
@ -3,7 +3,9 @@ package app
|
|||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -15,6 +17,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
|
||||
|
@ -28,6 +31,7 @@ var (
|
|||
blockSize int
|
||||
enabled bool
|
||||
verbose bool
|
||||
commit bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -36,6 +40,7 @@ func init() {
|
|||
flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block")
|
||||
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
|
||||
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
|
||||
flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit")
|
||||
}
|
||||
|
||||
func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
|
||||
|
@ -49,7 +54,7 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
|||
Coins: coins,
|
||||
})
|
||||
}
|
||||
|
||||
govGenesis := gov.DefaultGenesisState()
|
||||
// Default genesis state
|
||||
stakeGenesis := stake.DefaultGenesisState()
|
||||
var validators []stake.Validator
|
||||
|
@ -73,6 +78,7 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
|||
genesis := GenesisState{
|
||||
Accounts: genesisAccounts,
|
||||
StakeData: stakeGenesis,
|
||||
GovData: govGenesis,
|
||||
}
|
||||
|
||||
// Marshal genesis
|
||||
|
@ -112,6 +118,39 @@ func invariants(app *GaiaApp) []simulation.Invariant {
|
|||
}
|
||||
}
|
||||
|
||||
// Profile with:
|
||||
// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$ -SimulationCommit=true -cpuprofile cpu.out
|
||||
func BenchmarkFullGaiaSimulation(b *testing.B) {
|
||||
// Setup Gaia application
|
||||
var logger log.Logger
|
||||
logger = log.NewNopLogger()
|
||||
var db dbm.DB
|
||||
dir := os.TempDir()
|
||||
db, _ = dbm.NewGoLevelDB("Simulation", dir)
|
||||
defer func() {
|
||||
db.Close()
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
app := NewGaiaApp(logger, db, nil)
|
||||
|
||||
// Run randomized simulation
|
||||
// TODO parameterize numbers, save for a later PR
|
||||
simulation.SimulateFromSeed(
|
||||
b, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(app), // these shouldn't get ran
|
||||
numBlocks,
|
||||
blockSize,
|
||||
commit,
|
||||
)
|
||||
if commit {
|
||||
fmt.Println("GoLevelDB Stats")
|
||||
fmt.Println(db.Stats()["leveldb.stats"])
|
||||
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullGaiaSimulation(t *testing.T) {
|
||||
if !enabled {
|
||||
t.Skip("Skipping Gaia simulation")
|
||||
|
@ -136,9 +175,11 @@ func TestFullGaiaSimulation(t *testing.T) {
|
|||
invariants(app),
|
||||
numBlocks,
|
||||
blockSize,
|
||||
false,
|
||||
commit,
|
||||
)
|
||||
|
||||
if commit {
|
||||
fmt.Println("Database Size", db.Stats()["database.size"])
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
||||
|
|
|
@ -58,6 +58,13 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// Test --dry-run
|
||||
success := executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --dry-run", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
// Check state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// test autosequencing
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
@ -148,6 +155,10 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
|
||||
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1))
|
||||
|
||||
// Test --dry-run
|
||||
success := executeWrite(t, cvStr+" --dry-run", app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
|
||||
executeWrite(t, cvStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
|
@ -164,7 +175,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr))
|
||||
unbondStr += fmt.Sprintf(" --shares-amount=%v", "1")
|
||||
|
||||
success := executeWrite(t, unbondStr, app.DefaultKeyPass)
|
||||
success = executeWrite(t, unbondStr, app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
|
@ -211,6 +222,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
spStr += fmt.Sprintf(" --title=%s", "Test")
|
||||
spStr += fmt.Sprintf(" --description=%s", "test")
|
||||
|
||||
// Test --dry-run
|
||||
success := executeWrite(t, spStr+" --dry-run", app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
|
||||
executeWrite(t, spStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
|
|
|
@ -95,6 +95,8 @@ When you query an account balance with zero tokens, you will get this error: `No
|
|||
|
||||
### Send Tokens
|
||||
|
||||
The following command could be used to send coins from one account to another:
|
||||
|
||||
```bash
|
||||
gaiacli send \
|
||||
--amount=10faucetToken \
|
||||
|
@ -110,7 +112,7 @@ The `--amount` flag accepts the format `--amount=<value|coin_name>`.
|
|||
::: tip Note
|
||||
You may want to cap the maximum gas that can be consumed by the transaction via the `--gas` flag.
|
||||
If set to 0, the gas limit will be automatically estimated.
|
||||
Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.2.
|
||||
Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.0.
|
||||
:::
|
||||
|
||||
Now, view the updated balances of the origin and destination accounts:
|
||||
|
@ -126,6 +128,17 @@ You can also check your balance at a given block by using the `--block` flag:
|
|||
gaiacli account <account_cosmos> --block=<block_height>
|
||||
```
|
||||
|
||||
You can simulate a transaction without actually broadcasting it by appending the `--dry-run` flag to the command line:
|
||||
|
||||
```bash
|
||||
gaiacli send \
|
||||
--amount=10faucetToken \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name> \
|
||||
--to=<destination_cosmosaccaddr> \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
### Staking
|
||||
|
||||
#### Set up a Validator
|
||||
|
|
|
@ -135,3 +135,8 @@ func (vs *ValidatorSet) Jail(ctx sdk.Context, pubkey crypto.PubKey) {
|
|||
func (vs *ValidatorSet) Unjail(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// Implements sdk.ValidatorSet
|
||||
func (vs *ValidatorSet) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) sdk.Delegation {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
|
|
@ -88,17 +88,17 @@ func TestMsgQuiz(t *testing.T) {
|
|||
require.Equal(t, acc1, res1)
|
||||
|
||||
// Set the trend, submit a really cool quiz and check for reward
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, priv1) // result without reward
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, priv1) // reset the trend
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, priv1) // the same answer will nolonger do!
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do!
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, priv1) // earlier answer now relevant again
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, priv1) // expect to fail to set the trend to something which is not cool
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool
|
||||
}
|
||||
|
|
|
@ -74,13 +74,13 @@ func TestMsgMine(t *testing.T) {
|
|||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := GenerateMsgMine(addr1, 1, 2)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}})
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := GenerateMsgMine(addr1, 2, 3)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
|
||||
// Mine again - should be invalid
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"os"
|
||||
"bytes"
|
||||
"io"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/stretchr/testify/require"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmptyState(t *testing.T) {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
// ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tendermint/iavl"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// MultiStoreProof defines a collection of store proofs in a multi-store
|
||||
type MultiStoreProof struct {
|
||||
StoreInfos []storeInfo
|
||||
StoreName string
|
||||
RangeProof iavl.RangeProof
|
||||
}
|
||||
|
||||
// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos
|
||||
func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte {
|
||||
var rangeProof iavl.RangeProof
|
||||
cdc.MustUnmarshalBinary(iavlProof, &rangeProof)
|
||||
|
||||
msp := MultiStoreProof{
|
||||
StoreInfos: storeInfos,
|
||||
StoreName: storeName,
|
||||
RangeProof: rangeProof,
|
||||
}
|
||||
|
||||
proof := cdc.MustMarshalBinary(msp)
|
||||
return proof
|
||||
}
|
||||
|
||||
// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash
|
||||
func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) {
|
||||
var substoreCommitHash []byte
|
||||
var height int64
|
||||
for _, storeInfo := range storeInfos {
|
||||
if storeInfo.Name == storeName {
|
||||
substoreCommitHash = storeInfo.Core.CommitID.Hash
|
||||
height = storeInfo.Core.CommitID.Version
|
||||
}
|
||||
}
|
||||
if len(substoreCommitHash) == 0 {
|
||||
return nil, cmn.NewError("failed to get substore root commit hash by store name")
|
||||
}
|
||||
|
||||
ci := commitInfo{
|
||||
Version: height,
|
||||
StoreInfos: storeInfos,
|
||||
}
|
||||
|
||||
if !bytes.Equal(appHash, ci.Hash()) {
|
||||
return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash")
|
||||
}
|
||||
return substoreCommitHash, nil
|
||||
}
|
||||
|
||||
// VerifyRangeProof verify iavl RangeProof
|
||||
func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error {
|
||||
|
||||
// verify the proof to ensure data integrity.
|
||||
err := rangeProof.Verify(substoreCommitHash)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash")
|
||||
}
|
||||
|
||||
if len(value) != 0 {
|
||||
// verify existence proof
|
||||
err = rangeProof.VerifyItem(key, value)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in existence verification")
|
||||
}
|
||||
} else {
|
||||
// verify absence proof
|
||||
err = rangeProof.VerifyAbsence(key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in absence verification")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequireProof return whether proof is require for the subpath
|
||||
func RequireProof(subpath string) bool {
|
||||
// Currently, only when query subpath is "/store" or "/key", will proof be included in response.
|
||||
// If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212
|
||||
if subpath == "/store" || subpath == "/key" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tendermint/iavl"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVerifyMultiStoreCommitInfo(t *testing.T) {
|
||||
appHash, _ := hex.DecodeString("ebf3c1fb724d3458023c8fefef7b33add2fc1e84")
|
||||
|
||||
substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
|
||||
storeName := "acc"
|
||||
|
||||
var storeInfos []storeInfo
|
||||
|
||||
gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78")
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "gov",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: gocRootHash,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "main",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: nil,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828")
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "acc",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: accRootHash,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "ibc",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: nil,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4")
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "stake",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: stakeRootHash,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e")
|
||||
storeInfos = append(storeInfos, storeInfo{
|
||||
Name: "slashing",
|
||||
Core: storeCore{
|
||||
CommitID: CommitID{
|
||||
Version: 689,
|
||||
Hash: slashingRootHash,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, commitHash, substoreRootHash)
|
||||
|
||||
appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3")
|
||||
|
||||
_, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash)
|
||||
assert.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo")
|
||||
}
|
||||
|
||||
func TestVerifyRangeProof(t *testing.T) {
|
||||
tree := iavl.NewTree(nil, 0)
|
||||
|
||||
rand := cmn.NewRand()
|
||||
rand.Seed(0) // for determinism
|
||||
for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} {
|
||||
key := []byte{ikey}
|
||||
tree.Set(key, []byte(rand.Str(8)))
|
||||
}
|
||||
|
||||
root := tree.Hash()
|
||||
|
||||
key := []byte{0x32}
|
||||
val, proof, err := tree.GetWithProof(key)
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, val)
|
||||
assert.NotEmpty(t, proof)
|
||||
err = VerifyRangeProof(key, val, root, proof)
|
||||
assert.Nil(t, err)
|
||||
|
||||
key = []byte{0x40}
|
||||
val, proof, err = tree.GetWithProof(key)
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, val)
|
||||
assert.NotEmpty(t, proof)
|
||||
err = VerifyRangeProof(key, val, root, proof)
|
||||
assert.Nil(t, err)
|
||||
}
|
|
@ -291,6 +291,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
|
|||
// trim the path and make the query
|
||||
req.Path = subpath
|
||||
res := queryable.Query(req)
|
||||
|
||||
if !req.Prove || !RequireProof(subpath) {
|
||||
return res
|
||||
}
|
||||
|
||||
commitInfo, errMsg := getCommitInfo(rs.db, res.Height)
|
||||
if errMsg != nil {
|
||||
return sdk.ErrInternal(errMsg.Error()).QueryResult()
|
||||
}
|
||||
|
||||
res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
tmclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Wait for the next tendermint block from the Tendermint RPC
|
||||
|
@ -185,6 +186,17 @@ func WaitForRPC(laddr string) {
|
|||
}
|
||||
}
|
||||
|
||||
// ExtractPortFromAddress extract port from listenAddress
|
||||
// The listenAddress must be some strings like tcp://0.0.0.0:12345
|
||||
func ExtractPortFromAddress(listenAddress string) string {
|
||||
stringList := strings.Split(listenAddress, ":")
|
||||
length := len(stringList)
|
||||
if length != 3 {
|
||||
panic(fmt.Errorf("expected listen address: tcp://0.0.0.0:12345, got %s", listenAddress))
|
||||
}
|
||||
return stringList[2]
|
||||
}
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -105,7 +105,7 @@ func (aa *AccAddress) UnmarshalJSON(data []byte) error {
|
|||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
aa2, err := AccAddressFromBech32(s)
|
||||
|
|
|
@ -415,6 +415,14 @@ func MinDec(d1, d2 Dec) Dec {
|
|||
return d2
|
||||
}
|
||||
|
||||
// maximum decimal between two
|
||||
func MaxDec(d1, d2 Dec) Dec {
|
||||
if d1.LT(d2) {
|
||||
return d2
|
||||
}
|
||||
return d1
|
||||
}
|
||||
|
||||
// intended to be used with require/assert: require.True(DecEq(...))
|
||||
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, Dec, Dec) {
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
|
|
|
@ -75,6 +75,10 @@ type ValidatorSet interface {
|
|||
Slash(Context, crypto.PubKey, int64, int64, Dec)
|
||||
Jail(Context, crypto.PubKey) // jail a validator
|
||||
Unjail(Context, crypto.PubKey) // unjail a validator
|
||||
|
||||
// Delegation allows for getting a particular delegation for a given validator
|
||||
// and delegator outside the scope of the staking module.
|
||||
Delegation(Context, AccAddress, ValAddress) Delegation
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________
|
||||
|
@ -95,3 +99,13 @@ type DelegationSet interface {
|
|||
IterateDelegations(ctx Context, delegator AccAddress,
|
||||
fn func(index int64, delegation Delegation) (stop bool))
|
||||
}
|
||||
|
||||
// validator event hooks
|
||||
// These can be utilized to communicate between a staking keeper
|
||||
// and another keeper which must take particular actions when
|
||||
// validators are bonded and unbonded. The second keeper must implement
|
||||
// this interface, which then the staking keeper can call.
|
||||
type ValidatorHooks interface {
|
||||
OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded
|
||||
OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // Must be called when a validator begins unbonding
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
|||
return newCtx, err.Result(), true
|
||||
}
|
||||
|
||||
sigs := stdTx.GetSignatures()
|
||||
sigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice.
|
||||
signerAddrs := stdTx.GetSigners()
|
||||
msgs := tx.GetMsgs()
|
||||
|
||||
|
@ -88,10 +88,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
|||
|
||||
// check signature, return account with incremented nonce
|
||||
signBytes := StdSignBytes(newCtx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
|
||||
signerAcc, res := processSig(
|
||||
newCtx, am,
|
||||
signerAddr, sig, signBytes,
|
||||
)
|
||||
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytes, simulate)
|
||||
if !res.IsOK() {
|
||||
return newCtx, res, true
|
||||
}
|
||||
|
@ -149,24 +146,24 @@ func validateBasic(tx StdTx) (err sdk.Error) {
|
|||
// if the account doesn't have a pubkey, set it.
|
||||
func processSig(
|
||||
ctx sdk.Context, am AccountMapper,
|
||||
addr sdk.AccAddress, sig StdSignature, signBytes []byte) (
|
||||
addr sdk.AccAddress, sig StdSignature, signBytes []byte, simulate bool) (
|
||||
acc Account, res sdk.Result) {
|
||||
|
||||
// Get the account.
|
||||
acc = am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
|
||||
}
|
||||
|
||||
// Check account number.
|
||||
accnum := acc.GetAccountNumber()
|
||||
seq := acc.GetSequence()
|
||||
|
||||
// Check account number.
|
||||
if accnum != sig.AccountNumber {
|
||||
return nil, sdk.ErrInvalidSequence(
|
||||
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
|
||||
}
|
||||
|
||||
// Check and increment sequence number.
|
||||
seq := acc.GetSequence()
|
||||
// Check sequence number.
|
||||
if seq != sig.Sequence {
|
||||
return nil, sdk.ErrInvalidSequence(
|
||||
fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result()
|
||||
|
@ -176,33 +173,50 @@ func processSig(
|
|||
// Handle w/ #870
|
||||
panic(err)
|
||||
}
|
||||
// If pubkey is not known for account,
|
||||
// set it from the StdSignature.
|
||||
pubKey := acc.GetPubKey()
|
||||
if pubKey == nil {
|
||||
pubKey = sig.PubKey
|
||||
if pubKey == nil {
|
||||
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
|
||||
}
|
||||
if !bytes.Equal(pubKey.Address(), addr) {
|
||||
return nil, sdk.ErrInvalidPubKey(
|
||||
fmt.Sprintf("PubKey does not match Signer address %v", addr)).Result()
|
||||
pubKey, res := processPubKey(acc, sig, simulate)
|
||||
if !res.IsOK() {
|
||||
return nil, res
|
||||
}
|
||||
err = acc.SetPubKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
|
||||
}
|
||||
}
|
||||
|
||||
// Check sig.
|
||||
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
|
||||
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
|
||||
if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) {
|
||||
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func processPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, sdk.Result) {
|
||||
// If pubkey is not known for account,
|
||||
// set it from the StdSignature.
|
||||
pubKey := acc.GetPubKey()
|
||||
if simulate {
|
||||
// In simulate mode the transaction comes with no signatures, thus
|
||||
// if the account's pubkey is nil, both signature verification
|
||||
// and gasKVStore.Set() shall consume the largest amount, i.e.
|
||||
// it takes more gas to verifiy secp256k1 keys than ed25519 ones.
|
||||
if pubKey == nil {
|
||||
return secp256k1.GenPrivKey().PubKey(), sdk.Result{}
|
||||
}
|
||||
return pubKey, sdk.Result{}
|
||||
}
|
||||
if pubKey == nil {
|
||||
pubKey = sig.PubKey
|
||||
if pubKey == nil {
|
||||
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
|
||||
}
|
||||
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
|
||||
return nil, sdk.ErrInvalidPubKey(
|
||||
fmt.Sprintf("PubKey does not match Signer address %v", acc.GetAddress())).Result()
|
||||
}
|
||||
}
|
||||
return pubKey, sdk.Result{}
|
||||
}
|
||||
|
||||
func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {
|
||||
switch pubkey.(type) {
|
||||
case ed25519.PubKeyEd25519:
|
||||
|
|
|
@ -4,14 +4,14 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
|
||||
|
@ -567,3 +567,63 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
|
|||
acc2 = mapper.GetAccount(ctx, addr2)
|
||||
require.Nil(t, acc2.GetPubKey())
|
||||
}
|
||||
|
||||
func TestProcessPubKey(t *testing.T) {
|
||||
ms, capKey, _ := setupMultiStore()
|
||||
cdc := wire.NewCodec()
|
||||
RegisterBaseAccount(cdc)
|
||||
mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount)
|
||||
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger())
|
||||
// keys
|
||||
_, addr1 := privAndAddr()
|
||||
priv2, _ := privAndAddr()
|
||||
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
|
||||
type args struct {
|
||||
acc Account
|
||||
sig StdSignature
|
||||
simulate bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"no sigs, simulate off", args{acc1, StdSignature{}, false}, true},
|
||||
{"no sigs, simulate on", args{acc1, StdSignature{}, true}, false},
|
||||
{"pubkey doesn't match addr, simulate off", args{acc1, StdSignature{PubKey: priv2.PubKey()}, false}, true},
|
||||
{"pubkey doesn't match addr, simulate on", args{acc1, StdSignature{PubKey: priv2.PubKey()}, true}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := processPubKey(tt.args.acc, tt.args.sig, tt.args.simulate)
|
||||
require.Equal(t, tt.wantErr, !err.IsOK())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsumeSignatureVerificationGas(t *testing.T) {
|
||||
type args struct {
|
||||
meter sdk.GasMeter
|
||||
pubkey crypto.PubKey
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
gasConsumed int64
|
||||
wantPanic bool
|
||||
}{
|
||||
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), ed25519.GenPrivKey().PubKey()}, ed25519VerifyCost, false},
|
||||
{"PubKeySecp256k1", args{sdk.NewInfiniteGasMeter(), secp256k1.GenPrivKey().PubKey()}, secp256k1VerifyCost, false},
|
||||
{"unknown key", args{sdk.NewInfiniteGasMeter(), nil}, 0, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantPanic {
|
||||
require.Panics(t, func() { consumeSignatureVerificationGas(tt.args.meter, tt.args.pubkey) })
|
||||
} else {
|
||||
consumeSignatureVerificationGas(tt.args.meter, tt.args.pubkey)
|
||||
require.Equal(t, tt.args.meter.GasConsumed(), tt.gasConsumed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,3 +148,32 @@ func (ctx TxContext) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]by
|
|||
|
||||
return ctx.Sign(name, passphrase, msg)
|
||||
}
|
||||
|
||||
// BuildWithPubKey builds a single message to be signed from a TxContext given a set of
|
||||
// messages and attach the public key associated to the given name.
|
||||
// It returns an error if a fee is supplied but cannot be parsed or the key cannot be
|
||||
// retrieved.
|
||||
func (ctx TxContext) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error) {
|
||||
msg, err := ctx.Build(msgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := keybase.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigs := []auth.StdSignature{{
|
||||
AccountNumber: msg.AccountNumber,
|
||||
Sequence: msg.Sequence,
|
||||
PubKey: info.GetPubKey(),
|
||||
}}
|
||||
|
||||
return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ func QueryAccountRequestHandlerFn(
|
|||
|
||||
addr, err := sdk.AccAddressFromBech32(bech32addr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -52,14 +52,14 @@ func QueryAccountRequestHandlerFn(
|
|||
// decode the value
|
||||
account, err := decoder(res)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// print out whole account
|
||||
output, err := cdc.MarshalJSON(account)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ type (
|
|||
}
|
||||
|
||||
appTestCase struct {
|
||||
expSimPass bool
|
||||
expPass bool
|
||||
msgs []sdk.Msg
|
||||
accNums []int64
|
||||
|
@ -110,6 +111,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg1},
|
||||
accNums: []int64{0},
|
||||
accSeqs: []int64{0},
|
||||
expSimPass: true,
|
||||
expPass: true,
|
||||
privKeys: []crypto.PrivKey{priv1},
|
||||
expectedBalances: []expectedBalance{
|
||||
|
@ -121,13 +123,14 @@ func TestMsgSendWithAccounts(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg1, sendMsg2},
|
||||
accNums: []int64{0},
|
||||
accSeqs: []int64{0},
|
||||
expSimPass: false,
|
||||
expPass: false,
|
||||
privKeys: []crypto.PrivKey{priv1},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
|
||||
|
||||
for _, eb := range tc.expectedBalances {
|
||||
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
|
||||
|
@ -144,7 +147,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
|
|||
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, true, priv1)
|
||||
}
|
||||
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
|
@ -166,6 +169,7 @@ func TestMsgSendMultipleOut(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg2},
|
||||
accNums: []int64{0},
|
||||
accSeqs: []int64{0},
|
||||
expSimPass: true,
|
||||
expPass: true,
|
||||
privKeys: []crypto.PrivKey{priv1},
|
||||
expectedBalances: []expectedBalance{
|
||||
|
@ -177,7 +181,7 @@ func TestMsgSendMultipleOut(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
|
||||
|
||||
for _, eb := range tc.expectedBalances {
|
||||
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
|
||||
|
@ -208,6 +212,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg3},
|
||||
accNums: []int64{0, 2},
|
||||
accSeqs: []int64{0, 0},
|
||||
expSimPass: true,
|
||||
expPass: true,
|
||||
privKeys: []crypto.PrivKey{priv1, priv4},
|
||||
expectedBalances: []expectedBalance{
|
||||
|
@ -220,7 +225,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
|
||||
|
||||
for _, eb := range tc.expectedBalances {
|
||||
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
|
||||
|
@ -243,6 +248,7 @@ func TestMsgSendDependent(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg1},
|
||||
accNums: []int64{0},
|
||||
accSeqs: []int64{0},
|
||||
expSimPass: true,
|
||||
expPass: true,
|
||||
privKeys: []crypto.PrivKey{priv1},
|
||||
expectedBalances: []expectedBalance{
|
||||
|
@ -254,6 +260,7 @@ func TestMsgSendDependent(t *testing.T) {
|
|||
msgs: []sdk.Msg{sendMsg4},
|
||||
accNums: []int64{1},
|
||||
accSeqs: []int64{0},
|
||||
expSimPass: true,
|
||||
expPass: true,
|
||||
privKeys: []crypto.PrivKey{priv2},
|
||||
expectedBalances: []expectedBalance{
|
||||
|
@ -263,7 +270,7 @@ func TestMsgSendDependent(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
|
||||
|
||||
for _, eb := range tc.expectedBalances {
|
||||
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
cliclient "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
@ -31,6 +32,7 @@ type sendBody struct {
|
|||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
}
|
||||
|
||||
var msgCdc = wire.NewCodec()
|
||||
|
@ -40,6 +42,7 @@ func init() {
|
|||
}
|
||||
|
||||
// SendRequestHandlerFn - http request handler to send coins to a address
|
||||
// nolint: gocyclo
|
||||
func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// collect data
|
||||
|
@ -48,32 +51,32 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
|
|||
|
||||
to, err := sdk.AccAddressFromBech32(bech32addr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var m sendBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
err = msgCdc.UnmarshalJSON(body, &m)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
info, err := kb.Get(m.LocalAccountName)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// build message
|
||||
msg := client.BuildMsg(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount)
|
||||
if err != nil { // XXX rechecking same error ?
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -85,10 +88,20 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
|
|||
Sequence: m.Sequence,
|
||||
}
|
||||
|
||||
if m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, cliclient.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cliCtx = cliCtx.WithGasAdjustment(adjustment)
|
||||
|
||||
if utils.HasDryRunArg(r) || m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if utils.HasDryRunArg(r) {
|
||||
utils.WriteSimulationResponse(w, txCtx.Gas)
|
||||
return
|
||||
}
|
||||
txCtx = newCtx
|
||||
|
@ -96,19 +109,19 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
|
|||
|
||||
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, res)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
// SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both
|
||||
// accounts already exist.
|
||||
func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
fromKey := simulation.RandomKey(r, keys)
|
||||
fromAddr := sdk.AccAddress(fromKey.PubKey().Address())
|
||||
toKey := simulation.RandomKey(r, keys)
|
||||
|
@ -58,7 +58,7 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation
|
|||
Inputs: []bank.Input{bank.NewInput(fromAddr, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(toAddr, coins)},
|
||||
}
|
||||
sendAndVerifyMsgSend(t, app, mapper, msg, ctx, log, []crypto.PrivKey{fromKey})
|
||||
sendAndVerifyMsgSend(tb, app, mapper, msg, ctx, log, []crypto.PrivKey{fromKey})
|
||||
event("bank/sendAndVerifyMsgSend/ok")
|
||||
|
||||
return action, nil, nil
|
||||
|
@ -66,7 +66,7 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation
|
|||
}
|
||||
|
||||
// Sends and verifies the transition of a msg send. This fails if there are repeated inputs or outputs
|
||||
func sendAndVerifyMsgSend(t *testing.T, app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, log string, privkeys []crypto.PrivKey) {
|
||||
func sendAndVerifyMsgSend(tb testing.TB, app *baseapp.BaseApp, mapper auth.AccountMapper, msg bank.MsgSend, ctx sdk.Context, log string, privkeys []crypto.PrivKey) {
|
||||
initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs))
|
||||
initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs))
|
||||
AccountNumbers := make([]int64, len(msg.Inputs))
|
||||
|
@ -91,12 +91,12 @@ func sendAndVerifyMsgSend(t *testing.T, app *baseapp.BaseApp, mapper auth.Accoun
|
|||
// TODO: Do this in a more 'canonical' way
|
||||
fmt.Println(res)
|
||||
fmt.Println(log)
|
||||
t.FailNow()
|
||||
tb.FailNow()
|
||||
}
|
||||
|
||||
for i := 0; i < len(msg.Inputs); i++ {
|
||||
terminalInputCoins := mapper.GetAccount(ctx, msg.Inputs[i].Address).GetCoins()
|
||||
require.Equal(t,
|
||||
require.Equal(tb,
|
||||
initialInputAddrCoins[i].Minus(msg.Inputs[i].Coins),
|
||||
terminalInputCoins,
|
||||
fmt.Sprintf("Input #%d had an incorrect amount of coins\n%s", i, log),
|
||||
|
@ -104,11 +104,9 @@ func sendAndVerifyMsgSend(t *testing.T, app *baseapp.BaseApp, mapper auth.Accoun
|
|||
}
|
||||
for i := 0; i < len(msg.Outputs); i++ {
|
||||
terminalOutputCoins := mapper.GetAccount(ctx, msg.Outputs[i].Address).GetCoins()
|
||||
require.Equal(t,
|
||||
initialOutputAddrCoins[i].Plus(msg.Outputs[i].Coins),
|
||||
terminalOutputCoins,
|
||||
fmt.Sprintf("Output #%d had an incorrect amount of coins\n%s", i, log),
|
||||
)
|
||||
if !terminalOutputCoins.IsEqual(initialOutputAddrCoins[i].Plus(msg.Outputs[i].Coins)) {
|
||||
tb.Fatalf("Output #%d had an incorrect amount of coins\n%s", i, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,11 +77,11 @@ func postProposalHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.Hand
|
|||
msg := gov.NewMsgSubmitProposal(req.Title, req.Description, req.ProposalType, req.Proposer, req.InitialDeposit)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
|
||||
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -114,11 +114,11 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu
|
|||
msg := gov.NewMsgDeposit(req.Depositer, proposalID, req.Amount)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
|
||||
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -151,11 +151,11 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc
|
|||
msg := gov.NewMsgVote(req.Voter, proposalID, req.Option)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
|
||||
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -183,13 +183,13 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -216,14 +216,14 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(bechDepositerAddr) == 0 {
|
||||
err := errors.New("depositer address required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
|
||||
if err != nil {
|
||||
err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer)
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -236,13 +236,13 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -252,11 +252,11 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID}))
|
||||
if err != nil || len(res) == 0 {
|
||||
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
|
||||
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID)
|
||||
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -283,14 +283,14 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(bechVoterAddr) == 0 {
|
||||
err := errors.New("voter address required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
|
||||
if err != nil {
|
||||
err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter)
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -302,13 +302,13 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
}
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.QueryWithData("custom/gov/vote", bz)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -317,17 +317,17 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
if vote.Empty() {
|
||||
bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
|
||||
if err != nil || len(res) == 0 {
|
||||
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
|
||||
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID)
|
||||
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
w.Write(res)
|
||||
|
@ -343,7 +343,7 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
if len(strProposalID) == 0 {
|
||||
err := errors.New("proposalId required but not specified")
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -359,13 +359,13 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
}
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.QueryWithData("custom/gov/votes", bz)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
|
||||
if err != nil {
|
||||
err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter)
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
params.Voter = voterAddr
|
||||
|
@ -398,7 +398,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
|
||||
if err != nil {
|
||||
err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer)
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
params.Depositer = depositerAddr
|
||||
|
@ -408,7 +408,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus)
|
||||
if err != nil {
|
||||
err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus)
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
params.ProposalStatus = proposalStatus
|
||||
|
@ -423,7 +423,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -431,7 +431,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
res, err := cliCtx.QueryWithData("custom/gov/proposals", bz)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -20,17 +21,18 @@ type baseReq struct {
|
|||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
}
|
||||
|
||||
func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req interface{}) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
err = cdc.UnmarshalJSON(body, req)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -38,27 +40,27 @@ func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req inter
|
|||
|
||||
func (req baseReq) baseReqValidate(w http.ResponseWriter) bool {
|
||||
if len(req.Name) == 0 {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Name required but not specified")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Name required but not specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(req.Password) == 0 {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Password required but not specified")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Password required but not specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(req.ChainID) == 0 {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "ChainID required but not specified")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "ChainID required but not specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if req.AccountNumber < 0 {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Account Number required but not specified")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Account Number required but not specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if req.Sequence < 0 {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Sequence required but not specified")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Sequence required but not specified")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -66,7 +68,7 @@ func (req baseReq) baseReqValidate(w http.ResponseWriter) bool {
|
|||
|
||||
// TODO: Build this function out into a more generic base-request
|
||||
// (probably should live in client/lcd).
|
||||
func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) {
|
||||
func signAndBuild(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) {
|
||||
var err error
|
||||
txCtx := authctx.TxContext{
|
||||
Codec: cdc,
|
||||
|
@ -76,29 +78,39 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base
|
|||
Gas: baseReq.Gas,
|
||||
}
|
||||
|
||||
if baseReq.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg})
|
||||
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cliCtx = cliCtx.WithGasAdjustment(adjustment)
|
||||
|
||||
if utils.HasDryRunArg(r) || baseReq.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if utils.HasDryRunArg(r) {
|
||||
utils.WriteSimulationResponse(w, txCtx.Gas)
|
||||
return
|
||||
}
|
||||
txCtx = newCtx
|
||||
}
|
||||
txBytes, err := txCtx.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, res)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
@ -23,7 +21,25 @@ const (
|
|||
// SimulateMsgSubmitProposal simulates a msg Submit Proposal
|
||||
// Note: Currently doesn't ensure that the proposal txt is in JSON form
|
||||
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := gov.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
msg := simulationCreateMsgSubmitProposal(tb, r, keys, log)
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
// Update pool to keep invariants
|
||||
pool := sk.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom)))
|
||||
sk.SetPool(ctx, pool)
|
||||
write()
|
||||
}
|
||||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
||||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, keys []crypto.PrivKey, log string) gov.MsgSubmitProposal {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
deposit := randomDeposit(r)
|
||||
|
@ -34,25 +50,15 @@ func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operati
|
|||
addr,
|
||||
deposit,
|
||||
)
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := gov.NewHandler(k)(ctx, msg)
|
||||
if result.IsOK() {
|
||||
// Update pool to keep invariants
|
||||
pool := sk.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(deposit.AmountOf(denom)))
|
||||
sk.SetPool(ctx, pool)
|
||||
write()
|
||||
}
|
||||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
||||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action, nil, nil
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// SimulateMsgDeposit
|
||||
func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
proposalID, ok := randomProposalID(r, k, ctx)
|
||||
|
@ -61,7 +67,9 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
|||
}
|
||||
deposit := randomDeposit(r)
|
||||
msg := gov.NewMsgDeposit(addr, proposalID, deposit)
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := gov.NewHandler(k)(ctx, msg)
|
||||
if result.IsOK() {
|
||||
|
@ -79,7 +87,7 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
|||
|
||||
// SimulateMsgVote
|
||||
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
proposalID, ok := randomProposalID(r, k, ctx)
|
||||
|
@ -88,7 +96,9 @@ func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
|||
}
|
||||
option := randomVotingOption(r)
|
||||
msg := gov.NewMsgVote(addr, proposalID, option)
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := gov.NewHandler(k)(ctx, msg)
|
||||
if result.IsOK() {
|
||||
|
|
|
@ -70,10 +70,10 @@ func TestIBCMsgs(t *testing.T) {
|
|||
Sequence: 0,
|
||||
}
|
||||
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{0}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, emptyCoins)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{1}, false, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{1}, false, false, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, coins)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, false, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, false, false, priv1)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
@ -29,10 +30,12 @@ type transferBody struct {
|
|||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
}
|
||||
|
||||
// TransferRequestHandler - http request handler to transfer coins to a address
|
||||
// on a different chain via IBC
|
||||
// nolint: gocyclo
|
||||
func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
@ -41,26 +44,26 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
|
|||
|
||||
to, err := sdk.AccAddressFromBech32(bech32addr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var m transferBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalJSON(body, &m)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
info, err := kb.Get(m.LocalAccountName)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -76,10 +79,20 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
|
|||
Gas: m.Gas,
|
||||
}
|
||||
|
||||
if m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cliCtx = cliCtx.WithGasAdjustment(adjustment)
|
||||
|
||||
if utils.HasDryRunArg(r) || m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if utils.HasDryRunArg(r) {
|
||||
utils.WriteSimulationResponse(w, txCtx.Gas)
|
||||
return
|
||||
}
|
||||
txCtx = newCtx
|
||||
|
@ -87,19 +100,19 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
|
|||
|
||||
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(res)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -61,14 +61,14 @@ func TestCheckAndDeliverGenTx(t *testing.T) {
|
|||
SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()},
|
||||
true, privKeys[0],
|
||||
true, true, privKeys[0],
|
||||
)
|
||||
|
||||
// Signing a tx with the wrong privKey should result in an auth error
|
||||
res := SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[1].GetAccountNumber()}, []int64{accs[1].GetSequence() + 1},
|
||||
false, privKeys[1],
|
||||
true, false, privKeys[1],
|
||||
)
|
||||
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
|
@ -76,7 +76,7 @@ func TestCheckAndDeliverGenTx(t *testing.T) {
|
|||
SignCheckDeliver(
|
||||
t, mApp.BaseApp, []sdk.Msg{msg},
|
||||
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence() + 1},
|
||||
true, privKeys[0],
|
||||
true, true, privKeys[0],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -16,7 +17,6 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Simulate tests application by sending random messages.
|
||||
|
@ -28,34 +28,10 @@ func Simulate(
|
|||
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit)
|
||||
}
|
||||
|
||||
// SimulateFromSeed tests an application by running the provided
|
||||
// operations, testing the provided invariants, but using the provided seed.
|
||||
func SimulateFromSeed(
|
||||
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []Operation, setups []RandSetup,
|
||||
invariants []Invariant, numBlocks int, blockSize int, commit bool,
|
||||
) {
|
||||
log := fmt.Sprintf("Starting SimulateFromSeed with randomness created with seed %d", int(seed))
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
|
||||
unixTime := r.Int63n(int64(math.Pow(2, 40)))
|
||||
|
||||
// Set the timestamp for simulation
|
||||
timestamp := time.Unix(unixTime, 0)
|
||||
log = fmt.Sprintf("%s\nStarting the simulation from time %v, unixtime %v", log, timestamp.UTC().Format(time.UnixDate), timestamp.Unix())
|
||||
fmt.Printf("%s\n", log)
|
||||
timeDiff := maxTimePerBlock - minTimePerBlock
|
||||
|
||||
keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys)
|
||||
|
||||
// Setup event stats
|
||||
events := make(map[string]uint)
|
||||
event := func(what string) {
|
||||
log += "\nevent - " + what
|
||||
events[what]++
|
||||
}
|
||||
|
||||
func initChain(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress, setups []RandSetup, app *baseapp.BaseApp,
|
||||
appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage) (validators map[string]mockValidator) {
|
||||
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, keys, accs)})
|
||||
validators := make(map[string]mockValidator)
|
||||
validators = make(map[string]mockValidator)
|
||||
for _, validator := range res.Validators {
|
||||
validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
|
||||
}
|
||||
|
@ -64,83 +40,161 @@ func SimulateFromSeed(
|
|||
setups[i](r, keys)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func randTimestamp(r *rand.Rand) time.Time {
|
||||
unixTime := r.Int63n(int64(math.Pow(2, 40)))
|
||||
return time.Unix(unixTime, 0)
|
||||
}
|
||||
|
||||
// SimulateFromSeed tests an application by running the provided
|
||||
// operations, testing the provided invariants, but using the provided seed.
|
||||
func SimulateFromSeed(
|
||||
tb testing.TB, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []Operation, setups []RandSetup,
|
||||
invariants []Invariant, numBlocks int, blockSize int, commit bool,
|
||||
) {
|
||||
testingMode, t, b := getTestingMode(tb)
|
||||
log := fmt.Sprintf("Starting SimulateFromSeed with randomness created with seed %d", int(seed))
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
timestamp := randTimestamp(r)
|
||||
log = updateLog(testingMode, log, "Starting the simulation from time %v, unixtime %v", timestamp.UTC().Format(time.UnixDate), timestamp.Unix())
|
||||
fmt.Printf("%s\n", log)
|
||||
timeDiff := maxTimePerBlock - minTimePerBlock
|
||||
|
||||
keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys)
|
||||
|
||||
// Setup event stats
|
||||
events := make(map[string]uint)
|
||||
event := func(what string) {
|
||||
log = updateLog(testingMode, log, "event - %s", what)
|
||||
events[what]++
|
||||
}
|
||||
|
||||
validators := initChain(r, keys, accs, setups, app, appStateFn)
|
||||
|
||||
header := abci.Header{Height: 0, Time: timestamp}
|
||||
opCount := 0
|
||||
|
||||
request := abci.RequestBeginBlock{Header: header}
|
||||
|
||||
var pastTimes []time.Time
|
||||
var pastSigningValidators [][]abci.SigningValidator
|
||||
|
||||
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header, log)
|
||||
// These are operations which have been queued by previous operations
|
||||
operationQueue := make(map[int][]Operation)
|
||||
|
||||
for i := 0; i < numBlocks; i++ {
|
||||
if !testingMode {
|
||||
b.ResetTimer()
|
||||
}
|
||||
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, numBlocks)
|
||||
|
||||
for i := 0; i < numBlocks; i++ {
|
||||
// Log the header time for future lookup
|
||||
pastTimes = append(pastTimes, header.Time)
|
||||
pastSigningValidators = append(pastSigningValidators, request.LastCommitInfo.Validators)
|
||||
|
||||
// Run the BeginBlock handler
|
||||
app.BeginBlock(request)
|
||||
log = updateLog(testingMode, log, "BeginBlock")
|
||||
|
||||
log += "\nBeginBlock"
|
||||
|
||||
if testingMode {
|
||||
// Make sure invariants hold at beginning of block
|
||||
AssertAllInvariants(t, app, invariants, log)
|
||||
}
|
||||
|
||||
ctx := app.NewContext(false, header)
|
||||
thisBlockSize := getBlockSize(r, blockSize)
|
||||
|
||||
var thisBlockSize int
|
||||
load := r.Float64()
|
||||
switch {
|
||||
case load < 0.33:
|
||||
thisBlockSize = 0
|
||||
case load < 0.66:
|
||||
thisBlockSize = r.Intn(blockSize * 2)
|
||||
default:
|
||||
thisBlockSize = r.Intn(blockSize * 4)
|
||||
}
|
||||
// Run queued operations. Ignores blocksize if blocksize is too small
|
||||
log, numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), t, r, app, ctx, keys, log, event)
|
||||
log, numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, log, event)
|
||||
opCount += numQueuedOpsRan
|
||||
thisBlockSize -= numQueuedOpsRan
|
||||
for j := 0; j < thisBlockSize; j++ {
|
||||
logUpdate, futureOps, err := ops[r.Intn(len(ops))](t, r, app, ctx, keys, log, event)
|
||||
log += "\n" + logUpdate
|
||||
queueOperations(operationQueue, futureOps)
|
||||
|
||||
require.Nil(t, err, log)
|
||||
if onOperation {
|
||||
AssertAllInvariants(t, app, invariants, log)
|
||||
}
|
||||
if opCount%200 == 0 {
|
||||
fmt.Printf("\rSimulating... block %d/%d, operation %d.", header.Height, numBlocks, opCount)
|
||||
}
|
||||
opCount++
|
||||
}
|
||||
log, operations := blockSimulator(thisBlockSize, r, app, ctx, keys, log, header)
|
||||
opCount += operations
|
||||
|
||||
res := app.EndBlock(abci.RequestEndBlock{})
|
||||
header.Height++
|
||||
header.Time = header.Time.Add(time.Duration(minTimePerBlock) * time.Second).Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second)
|
||||
log = updateLog(testingMode, log, "EndBlock")
|
||||
|
||||
log += "\nEndBlock"
|
||||
|
||||
if testingMode {
|
||||
// Make sure invariants hold at end of block
|
||||
AssertAllInvariants(t, app, invariants, log)
|
||||
|
||||
}
|
||||
if commit {
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
// Generate a random RequestBeginBlock with the current validator set for the next block
|
||||
request = RandomRequestBeginBlock(t, r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, event, header, log)
|
||||
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header, log)
|
||||
|
||||
// Update the validator set
|
||||
validators = updateValidators(t, r, validators, res.ValidatorUpdates, event)
|
||||
validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
|
||||
}
|
||||
|
||||
fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds): %v\n", header.Height, header.Time)
|
||||
fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds), : %v, operations ran %d\n", header.Height, header.Time, opCount)
|
||||
DisplayEvents(events)
|
||||
}
|
||||
|
||||
// Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize
|
||||
// memory overhead
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []Operation, operationQueue map[int][]Operation, totalNumBlocks int) func(
|
||||
blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, log string, header abci.Header) (updatedLog string, opCount int) {
|
||||
return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
keys []crypto.PrivKey, log string, header abci.Header) (updatedLog string, opCount int) {
|
||||
for j := 0; j < blocksize; j++ {
|
||||
logUpdate, futureOps, err := ops[r.Intn(len(ops))](tb, r, app, ctx, keys, log, event)
|
||||
log = updateLog(testingMode, log, logUpdate)
|
||||
if err != nil {
|
||||
tb.Fatalf("error on operation %d within block %d, %v, log %s", header.Height, opCount, err, log)
|
||||
}
|
||||
|
||||
queueOperations(operationQueue, futureOps)
|
||||
if testingMode {
|
||||
if onOperation {
|
||||
AssertAllInvariants(t, app, invariants, log)
|
||||
}
|
||||
if opCount%50 == 0 {
|
||||
fmt.Printf("\rSimulating... block %d/%d, operation %d/%d.", header.Height, totalNumBlocks, opCount, blocksize)
|
||||
}
|
||||
}
|
||||
opCount++
|
||||
}
|
||||
return log, opCount
|
||||
}
|
||||
}
|
||||
|
||||
func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) {
|
||||
testingMode = false
|
||||
if _t, ok := tb.(*testing.T); ok {
|
||||
t = _t
|
||||
testingMode = true
|
||||
} else {
|
||||
b = tb.(*testing.B)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateLog(testingMode bool, log string, update string, args ...interface{}) (updatedLog string) {
|
||||
if testingMode {
|
||||
update = fmt.Sprintf(update, args...)
|
||||
return fmt.Sprintf("%s\n%s", log, update)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getBlockSize(r *rand.Rand, blockSize int) int {
|
||||
load := r.Float64()
|
||||
switch {
|
||||
case load < 0.33:
|
||||
return 0
|
||||
case load < 0.66:
|
||||
return r.Intn(blockSize * 2)
|
||||
default:
|
||||
return r.Intn(blockSize * 4)
|
||||
}
|
||||
}
|
||||
|
||||
// adds all future operations into the operation queue.
|
||||
func queueOperations(queuedOperations map[int][]Operation, futureOperations []FutureOperation) {
|
||||
if futureOperations == nil {
|
||||
|
@ -155,7 +209,7 @@ func queueOperations(queuedOperations map[int][]Operation, futureOperations []Fu
|
|||
}
|
||||
}
|
||||
|
||||
func runQueuedOperations(queueOperations map[int][]Operation, height int, t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
func runQueuedOperations(queueOperations map[int][]Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
privKeys []crypto.PrivKey, log string, event func(string)) (updatedLog string, numOpsRan int) {
|
||||
updatedLog = log
|
||||
if queuedOps, ok := queueOperations[height]; ok {
|
||||
|
@ -164,9 +218,12 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, t *tes
|
|||
// For now, queued operations cannot queue more operations.
|
||||
// If a need arises for us to support queued messages to queue more messages, this can
|
||||
// be changed.
|
||||
logUpdate, _, err := queuedOps[i](t, r, app, ctx, privKeys, updatedLog, event)
|
||||
updatedLog += "\n" + logUpdate
|
||||
require.Nil(t, err, updatedLog)
|
||||
logUpdate, _, err := queuedOps[i](tb, r, app, ctx, privKeys, updatedLog, event)
|
||||
updatedLog = fmt.Sprintf("%s\n%s", updatedLog, logUpdate)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, updatedLog)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
delete(queueOperations, height)
|
||||
return updatedLog, numOps
|
||||
|
@ -186,14 +243,13 @@ func getKeys(validators map[string]mockValidator) []string {
|
|||
}
|
||||
|
||||
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
|
||||
func RandomRequestBeginBlock(t *testing.T, r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
|
||||
pastTimes []time.Time, event func(string), header abci.Header, log string) abci.RequestBeginBlock {
|
||||
func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
|
||||
pastTimes []time.Time, pastSigningValidators [][]abci.SigningValidator, event func(string), header abci.Header, log string) abci.RequestBeginBlock {
|
||||
if len(validators) == 0 {
|
||||
return abci.RequestBeginBlock{Header: header}
|
||||
}
|
||||
signingValidators := make([]abci.SigningValidator, len(validators))
|
||||
i := 0
|
||||
|
||||
for _, key := range getKeys(validators) {
|
||||
mVal := validators[key]
|
||||
mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState)
|
||||
|
@ -219,28 +275,34 @@ func RandomRequestBeginBlock(t *testing.T, r *rand.Rand, validators map[string]m
|
|||
}
|
||||
i++
|
||||
}
|
||||
// TODO: Determine capacity before allocation
|
||||
evidence := make([]abci.Evidence, 0)
|
||||
// Anything but the first block
|
||||
if len(pastTimes) > 0 {
|
||||
for r.Float64() < evidenceFraction {
|
||||
height := header.Height
|
||||
time := header.Time
|
||||
vals := signingValidators
|
||||
if r.Float64() < pastEvidenceFraction {
|
||||
height = int64(r.Intn(int(header.Height)))
|
||||
time = pastTimes[height]
|
||||
vals = pastSigningValidators[height]
|
||||
}
|
||||
validator := signingValidators[r.Intn(len(signingValidators))].Validator
|
||||
var currentTotalVotingPower int64
|
||||
for _, mVal := range validators {
|
||||
currentTotalVotingPower += mVal.val.Power
|
||||
validator := vals[r.Intn(len(vals))].Validator
|
||||
var totalVotingPower int64
|
||||
for _, val := range vals {
|
||||
totalVotingPower += val.Validator.Power
|
||||
}
|
||||
evidence = append(evidence, abci.Evidence{
|
||||
Type: tmtypes.ABCIEvidenceTypeDuplicateVote,
|
||||
Validator: validator,
|
||||
Height: height,
|
||||
Time: time,
|
||||
TotalVotingPower: currentTotalVotingPower,
|
||||
TotalVotingPower: totalVotingPower,
|
||||
})
|
||||
event("beginblock/evidence")
|
||||
}
|
||||
}
|
||||
return abci.RequestBeginBlock{
|
||||
Header: header,
|
||||
LastCommitInfo: abci.LastCommitInfo{
|
||||
|
@ -258,11 +320,19 @@ func AssertAllInvariants(t *testing.T, app *baseapp.BaseApp, tests []Invariant,
|
|||
}
|
||||
|
||||
// updateValidators mimicks Tendermint's update logic
|
||||
func updateValidators(t *testing.T, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator {
|
||||
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator {
|
||||
for _, update := range updates {
|
||||
switch {
|
||||
case update.Power == 0:
|
||||
require.NotNil(t, current[string(update.PubKey.Data)], "tried to delete a nonexistent validator")
|
||||
// // TEMPORARY DEBUG CODE TO PROVE THAT THE OLD METHOD WAS BROKEN
|
||||
// // (i.e. didn't catch in the event of problem)
|
||||
// if val, ok := tb.(*testing.T); ok {
|
||||
// require.NotNil(val, current[string(update.PubKey.Data)])
|
||||
// }
|
||||
// // CORRECT CHECK
|
||||
// if _, ok := current[string(update.PubKey.Data)]; !ok {
|
||||
// tb.Fatalf("tried to delete a nonexistent validator")
|
||||
// }
|
||||
event("endblock/validatorupdates/kicked")
|
||||
delete(current, string(update.PubKey.Data))
|
||||
default:
|
||||
|
|
|
@ -23,7 +23,7 @@ type (
|
|||
// Operations can optionally provide a list of "FutureOperations" to run later
|
||||
// These will be ran at the beginning of the corresponding block.
|
||||
Operation func(
|
||||
t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
privKeys []crypto.PrivKey, log string, event func(string),
|
||||
) (action string, futureOperations []FutureOperation, err sdk.Error)
|
||||
|
||||
|
|
|
@ -71,13 +71,13 @@ func CheckGenTx(
|
|||
// returned.
|
||||
func SignCheckDeliver(
|
||||
t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64,
|
||||
seq []int64, expPass bool, priv ...crypto.PrivKey,
|
||||
seq []int64, expSimPass, expPass bool, priv ...crypto.PrivKey,
|
||||
) sdk.Result {
|
||||
tx := GenTx(msgs, accNums, seq, priv...)
|
||||
// Must simulate now as CheckTx doesn't run Msgs anymore
|
||||
res := app.Simulate(tx)
|
||||
|
||||
if expPass {
|
||||
if expSimPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
|
|
@ -79,7 +79,7 @@ func checkValidator(t *testing.T, mapp *mock.App, keeper stake.Keeper,
|
|||
}
|
||||
|
||||
func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper,
|
||||
addr sdk.ValAddress, expFound bool) ValidatorSigningInfo {
|
||||
addr sdk.ConsAddress, expFound bool) ValidatorSigningInfo {
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
signingInfo, found := keeper.getValidatorSigningInfo(ctxCheck, addr)
|
||||
require.Equal(t, expFound, found)
|
||||
|
@ -102,7 +102,7 @@ func TestSlashingMsgs(t *testing.T) {
|
|||
createValidatorMsg := stake.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
|
@ -113,9 +113,9 @@ func TestSlashingMsgs(t *testing.T) {
|
|||
unjailMsg := MsgUnjail{ValidatorAddr: sdk.ValAddress(validator.PubKey.Address())}
|
||||
|
||||
// no signing info yet
|
||||
checkValidatorSigningInfo(t, mapp, keeper, sdk.ValAddress(addr1), false)
|
||||
checkValidatorSigningInfo(t, mapp, keeper, sdk.ConsAddress(addr1), false)
|
||||
|
||||
// unjail should fail with unknown validator
|
||||
res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, priv1)
|
||||
res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, false, priv1)
|
||||
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ValAddress(pk.Address()))
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(pk.Address()))
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
res, err := cliCtx.QueryStore(key, storeName)
|
||||
|
|
|
@ -30,7 +30,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *wire
|
|||
return
|
||||
}
|
||||
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ValAddress(pk.Address()))
|
||||
key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(pk.Address()))
|
||||
|
||||
res, err := cliCtx.QueryStore(key, storeName)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
@ -33,37 +34,39 @@ type UnjailBody struct {
|
|||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var m UnjailBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(body, &m)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
info, err := kb.Get(m.LocalAccountName)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
valAddr, err := sdk.ValAddressFromBech32(m.ValidatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), valAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own validator address")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -77,10 +80,20 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI
|
|||
|
||||
msg := slashing.NewMsgUnjail(valAddr)
|
||||
|
||||
if m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cliCtx = cliCtx.WithGasAdjustment(adjustment)
|
||||
|
||||
if utils.HasDryRunArg(r) || m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if utils.HasDryRunArg(r) {
|
||||
utils.WriteSimulationResponse(w, txCtx.Gas)
|
||||
return
|
||||
}
|
||||
txCtx = newCtx
|
||||
|
@ -88,19 +101,19 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI
|
|||
|
||||
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own validator address")
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := json.MarshalIndent(res, "", " ")
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,25 @@ const (
|
|||
CodeInvalidValidator CodeType = 101
|
||||
CodeValidatorJailed CodeType = 102
|
||||
CodeValidatorNotJailed CodeType = 103
|
||||
CodeMissingSelfDelegation CodeType = 104
|
||||
)
|
||||
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
|
||||
}
|
||||
|
||||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
|
||||
}
|
||||
|
||||
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeValidatorJailed, "validator still jailed, cannot yet be unjailed")
|
||||
}
|
||||
|
||||
func ErrValidatorNotJailed(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeValidatorNotJailed, "validator not jailed, cannot be unjailed")
|
||||
}
|
||||
|
||||
func ErrMissingSelfDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeMissingSelfDelegation, "validator has no self-delegation; cannot be unjailed")
|
||||
}
|
||||
|
|
|
@ -19,35 +19,38 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
// Validators must submit a transaction to unjail itself after
|
||||
// having been jailed (and thus unbonded) for downtime
|
||||
func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
|
||||
|
||||
// Validator must exist
|
||||
validator := k.validatorSet.Validator(ctx, msg.ValidatorAddr)
|
||||
if validator == nil {
|
||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||
}
|
||||
|
||||
// cannot be unjailed if no self-delegation exists
|
||||
selfDel := k.validatorSet.Delegation(ctx, sdk.AccAddress(msg.ValidatorAddr), msg.ValidatorAddr)
|
||||
if selfDel == nil {
|
||||
return ErrMissingSelfDelegation(k.codespace).Result()
|
||||
}
|
||||
|
||||
if !validator.GetJailed() {
|
||||
return ErrValidatorNotJailed(k.codespace).Result()
|
||||
}
|
||||
|
||||
addr := sdk.ValAddress(validator.GetPubKey().Address())
|
||||
addr := sdk.ConsAddress(validator.GetPubKey().Address())
|
||||
|
||||
// Signing info must exist
|
||||
info, found := k.getValidatorSigningInfo(ctx, addr)
|
||||
if !found {
|
||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||
}
|
||||
|
||||
// Cannot be unjailed until out of jail
|
||||
// cannot be unjailed until out of jail
|
||||
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
|
||||
return ErrValidatorJailed(k.codespace).Result()
|
||||
}
|
||||
|
||||
// Update the starting height (so the validator can't be immediately jailed again)
|
||||
// update the starting height so the validator can't be immediately jailed
|
||||
// again
|
||||
info.StartHeight = ctx.BlockHeight()
|
||||
k.setValidatorSigningInfo(ctx, addr, info)
|
||||
|
||||
// Unjail the validator
|
||||
k.validatorSet.Unjail(ctx, validator.GetPubKey())
|
||||
|
||||
tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String()))
|
||||
|
|
|
@ -2,6 +2,7 @@ package slashing
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -19,7 +20,7 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
|
|||
got := stake.NewHandler(sk)(ctx, msg)
|
||||
require.True(t, got.IsOK())
|
||||
stake.EndBlocker(ctx, sk)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, sdk.ValAddress(addr)).GetPower()))
|
||||
|
||||
// assert non-jailed validator can't be unjailed
|
||||
|
@ -27,3 +28,64 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
|
|||
require.False(t, got.IsOK(), "allowed unjail of non-jailed validator")
|
||||
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), got.Code)
|
||||
}
|
||||
|
||||
func TestJailedValidatorDelegations(t *testing.T) {
|
||||
ctx, _, stakeKeeper, _, slashingKeeper := createTestInput(t)
|
||||
|
||||
stakeParams := stakeKeeper.GetParams(ctx)
|
||||
stakeParams.UnbondingTime = 0
|
||||
stakeKeeper.SetParams(ctx, stakeParams)
|
||||
|
||||
// create a validator
|
||||
amount := int64(10)
|
||||
valPubKey, bondAmount := pks[0], sdk.NewInt(amount)
|
||||
valAddr, consAddr := sdk.ValAddress(addrs[1]), sdk.ConsAddress(addrs[0])
|
||||
|
||||
msgCreateVal := newTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
|
||||
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
|
||||
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
|
||||
|
||||
// set dummy signing info
|
||||
newInfo := ValidatorSigningInfo{
|
||||
StartHeight: int64(0),
|
||||
IndexOffset: int64(0),
|
||||
JailedUntil: time.Unix(0, 0),
|
||||
SignedBlocksCounter: int64(0),
|
||||
}
|
||||
slashingKeeper.setValidatorSigningInfo(ctx, consAddr, newInfo)
|
||||
|
||||
// delegate tokens to the validator
|
||||
delAddr := sdk.AccAddress(addrs[2])
|
||||
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount)
|
||||
got = stake.NewHandler(stakeKeeper)(ctx, msgDelegate)
|
||||
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
unbondShares := sdk.NewDec(10)
|
||||
|
||||
// unbond validator total self-delegations (which should jail the validator)
|
||||
msgBeginUnbonding := stake.NewMsgBeginUnbonding(sdk.AccAddress(valAddr), valAddr, unbondShares)
|
||||
got = stake.NewHandler(stakeKeeper)(ctx, msgBeginUnbonding)
|
||||
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got: %v", got)
|
||||
|
||||
msgCompleteUnbonding := stake.NewMsgCompleteUnbonding(sdk.AccAddress(valAddr), valAddr)
|
||||
got = stake.NewHandler(stakeKeeper)(ctx, msgCompleteUnbonding)
|
||||
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got: %v", got)
|
||||
|
||||
// verify validator still exists and is jailed
|
||||
validator, found := stakeKeeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.True(t, validator.GetJailed())
|
||||
|
||||
// verify the validator cannot unjail itself
|
||||
got = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr))
|
||||
require.False(t, got.IsOK(), "expected jailed validator to not be able to unjail, got: %v", got)
|
||||
|
||||
// self-delegate to validator
|
||||
msgSelfDelegate := newTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
|
||||
got = stake.NewHandler(stakeKeeper)(ctx, msgSelfDelegate)
|
||||
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
|
||||
|
||||
// verify the validator can now unjail itself
|
||||
got = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr))
|
||||
require.True(t, got.IsOK(), "expected jailed validator to be able to unjail, got: %v", got)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Create a new slashing period when a validator is bonded
|
||||
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
slashingPeriod := ValidatorSlashingPeriod{
|
||||
ValidatorAddr: address,
|
||||
StartHeight: ctx.BlockHeight(),
|
||||
EndHeight: 0,
|
||||
SlashedSoFar: sdk.ZeroDec(),
|
||||
}
|
||||
k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod)
|
||||
}
|
||||
|
||||
// Mark the slashing period as having ended when a validator begins unbonding
|
||||
func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
slashingPeriod := k.getValidatorSlashingPeriodForHeight(ctx, address, ctx.BlockHeight())
|
||||
slashingPeriod.EndHeight = ctx.BlockHeight()
|
||||
k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod)
|
||||
}
|
||||
|
||||
// Wrapper struct for sdk.ValidatorHooks
|
||||
type ValidatorHooks struct {
|
||||
k Keeper
|
||||
}
|
||||
|
||||
// Assert implementation
|
||||
var _ sdk.ValidatorHooks = ValidatorHooks{}
|
||||
|
||||
// Return a sdk.ValidatorHooks interface over the wrapper struct
|
||||
func (k Keeper) ValidatorHooks() sdk.ValidatorHooks {
|
||||
return ValidatorHooks{k}
|
||||
}
|
||||
|
||||
// Implements sdk.ValidatorHooks
|
||||
func (v ValidatorHooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
v.k.onValidatorBonded(ctx, address)
|
||||
}
|
||||
|
||||
// Implements sdk.ValidatorHooks
|
||||
func (v ValidatorHooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) {
|
||||
v.k.onValidatorBeginUnbonding(ctx, address)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestHookOnValidatorBonded(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
keeper.onValidatorBonded(ctx, addr)
|
||||
period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight())
|
||||
require.Equal(t, ValidatorSlashingPeriod{addr, ctx.BlockHeight(), 0, sdk.ZeroDec()}, period)
|
||||
}
|
||||
|
||||
func TestHookOnValidatorBeginUnbonding(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
keeper.onValidatorBonded(ctx, addr)
|
||||
keeper.onValidatorBeginUnbonding(ctx, addr)
|
||||
period := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, ctx.BlockHeight())
|
||||
require.Equal(t, ValidatorSlashingPeriod{addr, ctx.BlockHeight(), ctx.BlockHeight(), sdk.ZeroDec()}, period)
|
||||
}
|
|
@ -40,7 +40,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
|
|||
logger := ctx.Logger().With("module", "x/slashing")
|
||||
time := ctx.BlockHeader().Time
|
||||
age := time.Sub(timestamp)
|
||||
address := sdk.ValAddress(addr)
|
||||
address := sdk.ConsAddress(addr)
|
||||
pubkey, err := k.getPubkey(ctx, addr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Validator address %v not found", addr))
|
||||
|
@ -56,8 +56,14 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
|
|||
// 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))
|
||||
|
||||
// Cap the amount slashed to the penalty for the worst infraction
|
||||
// within the slashing period when this infraction was committed
|
||||
fraction := k.SlashFractionDoubleSign(ctx)
|
||||
revisedFraction := k.capBySlashingPeriod(ctx, address, fraction, infractionHeight)
|
||||
logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction))
|
||||
|
||||
// Slash validator
|
||||
k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, k.SlashFractionDoubleSign(ctx))
|
||||
k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, revisedFraction)
|
||||
|
||||
// Jail validator
|
||||
k.validatorSet.Jail(ctx, pubkey)
|
||||
|
@ -76,7 +82,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
|
|||
func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) {
|
||||
logger := ctx.Logger().With("module", "x/slashing")
|
||||
height := ctx.BlockHeight()
|
||||
address := sdk.ValAddress(addr)
|
||||
address := sdk.ConsAddress(addr)
|
||||
pubkey, err := k.getPubkey(ctx, addr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Validator address %v not found", addr))
|
||||
|
@ -169,7 +175,3 @@ func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) {
|
|||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(getAddrPubkeyRelationKey(addr))
|
||||
}
|
||||
|
||||
func getAddrPubkeyRelationKey(address []byte) []byte {
|
||||
return append([]byte{0x03}, address...)
|
||||
}
|
||||
|
|
|
@ -24,13 +24,14 @@ func TestHandleDoubleSign(t *testing.T) {
|
|||
|
||||
// initial setup
|
||||
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
|
||||
amtInt := int64(100)
|
||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(sdk.ValAddress(addr), val, amt))
|
||||
require.True(t, got.IsOK())
|
||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||
keeper.AddValidators(ctx, validatorUpdates)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, sdk.ValAddress(addr)).GetPower()))
|
||||
|
||||
// handle a signature to set signing info
|
||||
|
@ -58,12 +59,68 @@ func TestHandleDoubleSign(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
// Test that the amount a validator is slashed for multiple double signs
|
||||
// is correctly capped by the slashing period in which they were committed
|
||||
func TestSlashingPeriodCap(t *testing.T) {
|
||||
|
||||
// initial setup
|
||||
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
|
||||
amtInt := int64(100)
|
||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
|
||||
require.True(t, got.IsOK())
|
||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||
keeper.AddValidators(ctx, validatorUpdates)
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
|
||||
|
||||
// handle a signature to set signing info
|
||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
|
||||
|
||||
// double sign less than max age
|
||||
keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt)
|
||||
|
||||
// should be jailed
|
||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||
// update block height
|
||||
ctx = ctx.WithBlockHeight(int64(1))
|
||||
// unjail to measure power
|
||||
sk.Unjail(ctx, val)
|
||||
// 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, val.Address(), 0, time.Unix(0, 0), amtInt)
|
||||
// should be jailed
|
||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||
// update block height
|
||||
ctx = ctx.WithBlockHeight(int64(2))
|
||||
// unjail to measure power
|
||||
sk.Unjail(ctx, val)
|
||||
// power should be equal, no more should have been slashed
|
||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
||||
|
||||
// double sign again, new slashing period
|
||||
keeper.handleDoubleSign(ctx, val.Address(), 2, time.Unix(0, 0), amtInt)
|
||||
// should be jailed
|
||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||
// unjail to measure power
|
||||
sk.Unjail(ctx, val)
|
||||
// power should be reduced
|
||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
|
||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
||||
}
|
||||
|
||||
// Test a validator through uptime, downtime, revocation,
|
||||
// unrevocation, starting height reset, and revocation again
|
||||
func TestHandleAbsentValidator(t *testing.T) {
|
||||
|
||||
// initial setup
|
||||
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
|
||||
amtInt := int64(100)
|
||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||
sh := stake.NewHandler(sk)
|
||||
|
@ -72,9 +129,9 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
require.True(t, got.IsOK())
|
||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||
keeper.AddValidators(ctx, validatorUpdates)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, sdk.ValAddress(addr)).GetPower()))
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||
require.False(t, found)
|
||||
require.Equal(t, int64(0), info.StartHeight)
|
||||
require.Equal(t, int64(0), info.IndexOffset)
|
||||
|
@ -89,7 +146,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
ctx = ctx.WithBlockHeight(height)
|
||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
|
||||
}
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
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), info.SignedBlocksCounter)
|
||||
|
@ -99,7 +156,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
ctx = ctx.WithBlockHeight(height)
|
||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||
}
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
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), info.SignedBlocksCounter)
|
||||
|
@ -113,14 +170,14 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
// 501st block missed
|
||||
ctx = ctx.WithBlockHeight(height)
|
||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
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)-1, info.SignedBlocksCounter)
|
||||
|
||||
// validator should have been jailed
|
||||
validator, _ = sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||
|
||||
// unrevocation should fail prior to jail expiration
|
||||
got = slh(ctx, NewMsgUnjail(sdk.ValAddress(addr)))
|
||||
|
@ -141,7 +198,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
require.Equal(t, int64(amtInt)-slashAmt, pool.BondedTokens.RoundInt64())
|
||||
|
||||
// validator start height should have been changed
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||
require.True(t, found)
|
||||
require.Equal(t, height, info.StartHeight)
|
||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
||||
|
@ -167,7 +224,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||
}
|
||||
validator, _ = sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||
}
|
||||
|
||||
// Test a new validator entering the validator set
|
||||
|
@ -182,7 +239,7 @@ func TestHandleNewValidator(t *testing.T) {
|
|||
require.True(t, got.IsOK())
|
||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||
keeper.AddValidators(ctx, validatorUpdates)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
|
||||
require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, sdk.ValAddress(addr)).GetPower())
|
||||
|
||||
// 1000 first blocks not a validator
|
||||
|
@ -193,7 +250,7 @@ func TestHandleNewValidator(t *testing.T) {
|
|||
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
|
||||
keeper.handleValidatorSignature(ctx, val.Address(), 100, false)
|
||||
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(keeper.SignedBlocksWindow(ctx)+1), info.StartHeight)
|
||||
require.Equal(t, int64(2), info.IndexOffset)
|
||||
|
@ -236,7 +293,7 @@ func TestHandleAlreadyJailed(t *testing.T) {
|
|||
|
||||
// validator should have been jailed and slashed
|
||||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||
|
||||
// validator should have been slashed
|
||||
require.Equal(t, int64(amtInt-1), validator.GetTokens().RoundInt64())
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// key prefix bytes
|
||||
var (
|
||||
ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info
|
||||
ValidatorSigningBitArrayKey = []byte{0x02} // Prefix for signature bit array
|
||||
ValidatorSlashingPeriodKey = []byte{0x03} // Prefix for slashing period
|
||||
AddrPubkeyRelationKey = []byte{0x04} // Prefix for address-pubkey relation
|
||||
)
|
||||
|
||||
// stored by *Tendermint* address (not owner address)
|
||||
func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
|
||||
return append(ValidatorSigningInfoKey, v.Bytes()...)
|
||||
}
|
||||
|
||||
// stored by *Tendermint* address (not owner address)
|
||||
func GetValidatorSigningBitArrayKey(v sdk.ConsAddress, i int64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, uint64(i))
|
||||
return append(ValidatorSigningBitArrayKey, append(v.Bytes(), b...)...)
|
||||
}
|
||||
|
||||
// stored by *Tendermint* address (not owner address)
|
||||
func GetValidatorSlashingPeriodPrefix(v sdk.ConsAddress) []byte {
|
||||
return append(ValidatorSlashingPeriodKey, v.Bytes()...)
|
||||
}
|
||||
|
||||
// stored by *Tendermint* address (not owner address) followed by start height
|
||||
func GetValidatorSlashingPeriodKey(v sdk.ConsAddress, startHeight int64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, uint64(startHeight))
|
||||
return append(GetValidatorSlashingPeriodPrefix(v), b...)
|
||||
}
|
||||
|
||||
func getAddrPubkeyRelationKey(address []byte) []byte {
|
||||
return append(AddrPubkeyRelationKey, address...)
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -9,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ValAddress) (info ValidatorSigningInfo, found bool) {
|
||||
func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info ValidatorSigningInfo, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := store.Get(GetValidatorSigningInfoKey(address))
|
||||
if bz == nil {
|
||||
|
@ -22,14 +21,14 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ValAddress)
|
|||
}
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ValAddress, info ValidatorSigningInfo) {
|
||||
func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(info)
|
||||
store.Set(GetValidatorSigningInfoKey(address), bz)
|
||||
}
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.ValAddress, index int64) (signed bool) {
|
||||
func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (signed bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := store.Get(GetValidatorSigningBitArrayKey(address, index))
|
||||
if bz == nil {
|
||||
|
@ -42,7 +41,7 @@ func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.ValAddr
|
|||
}
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ValAddress, index int64, signed bool) {
|
||||
func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, signed bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(signed)
|
||||
store.Set(GetValidatorSigningBitArrayKey(address, index), bz)
|
||||
|
@ -71,15 +70,3 @@ func (i ValidatorSigningInfo) HumanReadableString() string {
|
|||
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, signed blocks counter: %d",
|
||||
i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter)
|
||||
}
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func GetValidatorSigningInfoKey(v sdk.ValAddress) []byte {
|
||||
return append([]byte{0x01}, v.Bytes()...)
|
||||
}
|
||||
|
||||
// Stored by *validator* address (not owner address)
|
||||
func GetValidatorSigningBitArrayKey(v sdk.ValAddress, i int64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, uint64(i))
|
||||
return append([]byte{0x02}, append(v.Bytes(), b...)...)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
func TestGetSetValidatorSigningInfo(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]))
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
|
||||
require.False(t, found)
|
||||
newInfo := ValidatorSigningInfo{
|
||||
StartHeight: int64(4),
|
||||
|
@ -19,8 +19,8 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
|
|||
JailedUntil: time.Unix(2, 0),
|
||||
SignedBlocksCounter: int64(10),
|
||||
}
|
||||
keeper.setValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]), newInfo)
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]))
|
||||
keeper.setValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo)
|
||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
|
||||
require.True(t, found)
|
||||
require.Equal(t, info.StartHeight, int64(4))
|
||||
require.Equal(t, info.IndexOffset, int64(3))
|
||||
|
@ -30,9 +30,9 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
|
|||
|
||||
func TestGetSetValidatorSigningBitArray(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0)
|
||||
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
||||
require.False(t, signed) // treat empty key as unsigned
|
||||
keeper.setValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0, true)
|
||||
signed = keeper.getValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0)
|
||||
keeper.setValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
|
||||
signed = keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
||||
require.True(t, signed) // now should be signed
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
@ -17,11 +15,13 @@ import (
|
|||
|
||||
// SimulateMsgUnjail
|
||||
func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
address := sdk.ValAddress(key.PubKey().Address())
|
||||
msg := slashing.NewMsgUnjail(address)
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := slashing.NewHandler(k)(ctx, msg)
|
||||
if result.IsOK() {
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Cap an infraction's slash amount by the slashing period in which it was committed
|
||||
func (k Keeper) capBySlashingPeriod(ctx sdk.Context, address sdk.ConsAddress, fraction sdk.Dec, infractionHeight int64) (revisedFraction sdk.Dec) {
|
||||
|
||||
// Fetch the newest slashing period starting before this infraction was committed
|
||||
slashingPeriod := k.getValidatorSlashingPeriodForHeight(ctx, address, infractionHeight)
|
||||
|
||||
// Sanity check
|
||||
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))
|
||||
}
|
||||
|
||||
// Calculate the updated total slash amount
|
||||
// This is capped at the slashing fraction for the worst infraction within this slashing period
|
||||
totalToSlash := sdk.MaxDec(slashingPeriod.SlashedSoFar, fraction)
|
||||
|
||||
// Calculate the remainder which we now must slash
|
||||
revisedFraction = totalToSlash.Sub(slashingPeriod.SlashedSoFar)
|
||||
|
||||
// Update the slashing period struct
|
||||
slashingPeriod.SlashedSoFar = totalToSlash
|
||||
k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Stored by validator Tendermint address (not owner address)
|
||||
// This function retrieves the most recent slashing period starting
|
||||
// before a particular height - so the slashing period that was "in effect"
|
||||
// at the time of an infraction committed at that height.
|
||||
func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk.ConsAddress, height int64) (slashingPeriod ValidatorSlashingPeriod) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
// Get the most recent slashing period at or before the infraction height
|
||||
start := GetValidatorSlashingPeriodPrefix(address)
|
||||
end := sdk.PrefixEndBytes(GetValidatorSlashingPeriodKey(address, height))
|
||||
iterator := store.ReverseIterator(start, end)
|
||||
if !iterator.Valid() {
|
||||
panic("expected to find slashing period, but none was found")
|
||||
}
|
||||
slashingPeriod = k.unmarshalSlashingPeriodKeyValue(iterator.Key(), iterator.Value())
|
||||
return
|
||||
}
|
||||
|
||||
// Stored by validator Tendermint address (not owner address)
|
||||
// This function sets a validator slashing period for a particular validator,
|
||||
// start height, end height, and current slashed-so-far total, or updates
|
||||
// an existing slashing period for the same validator and start height.
|
||||
func (k Keeper) addOrUpdateValidatorSlashingPeriod(ctx sdk.Context, slashingPeriod ValidatorSlashingPeriod) {
|
||||
slashingPeriodValue := ValidatorSlashingPeriodValue{
|
||||
EndHeight: slashingPeriod.EndHeight,
|
||||
SlashedSoFar: slashingPeriod.SlashedSoFar,
|
||||
}
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinary(slashingPeriodValue)
|
||||
store.Set(GetValidatorSlashingPeriodKey(slashingPeriod.ValidatorAddr, slashingPeriod.StartHeight), bz)
|
||||
}
|
||||
|
||||
// Unmarshal key/value into a ValidatorSlashingPeriod
|
||||
func (k Keeper) unmarshalSlashingPeriodKeyValue(key []byte, value []byte) ValidatorSlashingPeriod {
|
||||
var slashingPeriodValue ValidatorSlashingPeriodValue
|
||||
k.cdc.MustUnmarshalBinary(value, &slashingPeriodValue)
|
||||
address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen])
|
||||
startHeight := int64(binary.LittleEndian.Uint64(key[1+sdk.AddrLen : 1+sdk.AddrLen+8]))
|
||||
return ValidatorSlashingPeriod{
|
||||
ValidatorAddr: address,
|
||||
StartHeight: startHeight,
|
||||
EndHeight: slashingPeriodValue.EndHeight,
|
||||
SlashedSoFar: slashingPeriodValue.SlashedSoFar,
|
||||
}
|
||||
}
|
||||
|
||||
// Construct a new `ValidatorSlashingPeriod` struct
|
||||
func NewValidatorSlashingPeriod(startHeight int64, endHeight int64, slashedSoFar sdk.Dec) ValidatorSlashingPeriod {
|
||||
return ValidatorSlashingPeriod{
|
||||
StartHeight: startHeight,
|
||||
EndHeight: endHeight,
|
||||
SlashedSoFar: slashedSoFar,
|
||||
}
|
||||
}
|
||||
|
||||
// Slashing period for a validator
|
||||
type ValidatorSlashingPeriod struct {
|
||||
ValidatorAddr sdk.ConsAddress `json:"validator_addr"` // validator which this slashing period is for
|
||||
StartHeight int64 `json:"start_height"` // starting height of the slashing period
|
||||
EndHeight int64 `json:"end_height"` // ending height of the slashing period, or sentinel value of 0 for in-progress
|
||||
SlashedSoFar sdk.Dec `json:"slashed_so_far"` // fraction of validator stake slashed so far in this slashing period
|
||||
}
|
||||
|
||||
// Value part of slashing period (validator address & start height are stored in the key)
|
||||
type ValidatorSlashingPeriodValue struct {
|
||||
EndHeight int64 `json:"end_height"`
|
||||
SlashedSoFar sdk.Dec `json:"slashed_so_far"`
|
||||
}
|
||||
|
||||
// Return human readable slashing period
|
||||
func (p ValidatorSlashingPeriod) HumanReadableString() string {
|
||||
return fmt.Sprintf("Start height: %d, end height: %d, slashed so far: %v",
|
||||
p.StartHeight, p.EndHeight, p.SlashedSoFar)
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestGetSetValidatorSlashingPeriod(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
height := int64(5)
|
||||
require.Panics(t, func() { keeper.getValidatorSlashingPeriodForHeight(ctx, addr, height) })
|
||||
newPeriod := ValidatorSlashingPeriod{
|
||||
ValidatorAddr: addr,
|
||||
StartHeight: height,
|
||||
EndHeight: height + 10,
|
||||
SlashedSoFar: sdk.ZeroDec(),
|
||||
}
|
||||
keeper.addOrUpdateValidatorSlashingPeriod(ctx, newPeriod)
|
||||
|
||||
// Get at start height
|
||||
retrieved := keeper.getValidatorSlashingPeriodForHeight(ctx, addr, height)
|
||||
require.Equal(t, newPeriod, retrieved)
|
||||
|
||||
// Get after start height (works)
|
||||
retrieved = keeper.getValidatorSlashingPeriodForHeight(ctx, addr, int64(6))
|
||||
require.Equal(t, newPeriod, retrieved)
|
||||
|
||||
// Get before start height (panic)
|
||||
require.Panics(t, func() { keeper.getValidatorSlashingPeriodForHeight(ctx, addr, int64(0)) })
|
||||
|
||||
// Get after end height (panic)
|
||||
newPeriod.EndHeight = int64(4)
|
||||
keeper.addOrUpdateValidatorSlashingPeriod(ctx, newPeriod)
|
||||
require.Panics(t, func() { keeper.capBySlashingPeriod(ctx, addr, sdk.ZeroDec(), height) })
|
||||
|
||||
// Back to old end height
|
||||
newPeriod.EndHeight = height + 10
|
||||
keeper.addOrUpdateValidatorSlashingPeriod(ctx, newPeriod)
|
||||
|
||||
// Set a new, later period
|
||||
anotherPeriod := ValidatorSlashingPeriod{
|
||||
ValidatorAddr: addr,
|
||||
StartHeight: height + 1,
|
||||
EndHeight: height + 11,
|
||||
SlashedSoFar: sdk.ZeroDec(),
|
||||
}
|
||||
keeper.addOrUpdateValidatorSlashingPeriod(ctx, anotherPeriod)
|
||||
|
||||
// Old period retrieved for prior height
|
||||
retrieved = keeper.getValidatorSlashingPeriodForHeight(ctx, addr, height)
|
||||
require.Equal(t, newPeriod, retrieved)
|
||||
|
||||
// New period retrieved at new height
|
||||
retrieved = keeper.getValidatorSlashingPeriodForHeight(ctx, addr, height+1)
|
||||
require.Equal(t, anotherPeriod, retrieved)
|
||||
}
|
||||
|
||||
func TestValidatorSlashingPeriodCap(t *testing.T) {
|
||||
ctx, _, _, _, keeper := createTestInput(t)
|
||||
addr := sdk.ConsAddress(addrs[0])
|
||||
height := int64(5)
|
||||
newPeriod := ValidatorSlashingPeriod{
|
||||
ValidatorAddr: addr,
|
||||
StartHeight: height,
|
||||
EndHeight: height + 10,
|
||||
SlashedSoFar: sdk.ZeroDec(),
|
||||
}
|
||||
keeper.addOrUpdateValidatorSlashingPeriod(ctx, newPeriod)
|
||||
half := sdk.NewDec(1).Quo(sdk.NewDec(2))
|
||||
|
||||
// First slash should be full
|
||||
fractionA := keeper.capBySlashingPeriod(ctx, addr, half, height)
|
||||
require.True(t, fractionA.Equal(half))
|
||||
|
||||
// Second slash should be capped
|
||||
fractionB := keeper.capBySlashingPeriod(ctx, addr, half, height)
|
||||
require.True(t, fractionB.Equal(sdk.ZeroDec()))
|
||||
|
||||
// Third slash should be capped to difference
|
||||
fractionC := keeper.capBySlashingPeriod(ctx, addr, sdk.OneDec(), height)
|
||||
require.True(t, fractionC.Equal(half))
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -30,10 +31,10 @@ var (
|
|||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"),
|
||||
}
|
||||
addrs = []sdk.AccAddress{
|
||||
sdk.AccAddress(pks[0].Address()),
|
||||
sdk.AccAddress(pks[1].Address()),
|
||||
sdk.AccAddress(pks[2].Address()),
|
||||
addrs = []sdk.ValAddress{
|
||||
sdk.ValAddress(pks[0].Address()),
|
||||
sdk.ValAddress(pks[1].Address()),
|
||||
sdk.ValAddress(pks[2].Address()),
|
||||
}
|
||||
initCoins = sdk.NewInt(200)
|
||||
)
|
||||
|
@ -61,7 +62,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para
|
|||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
||||
err := ms.LoadLatestVersion()
|
||||
require.Nil(t, err)
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout))
|
||||
ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewTMLogger(os.Stdout))
|
||||
cdc := createTestCodec()
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
|
||||
ck := bank.NewKeeper(accountMapper)
|
||||
|
@ -75,7 +76,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para
|
|||
require.Nil(t, err)
|
||||
|
||||
for _, addr := range addrs {
|
||||
_, _, err = ck.AddCoins(ctx, addr, sdk.Coins{
|
||||
_, _, err = ck.AddCoins(ctx, sdk.AccAddress(addr), sdk.Coins{
|
||||
{sk.GetParams(ctx).BondDenom, initCoins},
|
||||
})
|
||||
}
|
||||
|
@ -108,3 +109,11 @@ func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt
|
|||
Delegation: sdk.Coin{"steak", amt},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) stake.MsgDelegate {
|
||||
return stake.MsgDelegate{
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorAddr: valAddr,
|
||||
Delegation: sdk.Coin{"steak", delAmount},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ func TestBeginBlocker(t *testing.T) {
|
|||
require.True(t, got.IsOK())
|
||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||
keeper.AddValidators(ctx, validatorUpdates)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, sdk.ValAddress(addr)).GetPower()))
|
||||
|
||||
val := abci.Validator{
|
||||
|
@ -40,7 +40,7 @@ func TestBeginBlocker(t *testing.T) {
|
|||
}
|
||||
BeginBlocker(ctx, req, keeper)
|
||||
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(pk.Address()))
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address()))
|
||||
require.True(t, found)
|
||||
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
|
||||
require.Equal(t, int64(1), info.IndexOffset)
|
||||
|
@ -80,5 +80,5 @@ func TestBeginBlocker(t *testing.T) {
|
|||
// validator should be jailed
|
||||
validator, found := sk.GetValidatorByPubKey(ctx, pk)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ func TestStakeMsgs(t *testing.T) {
|
|||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
|
||||
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mApp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
|
@ -145,7 +145,7 @@ func TestStakeMsgs(t *testing.T) {
|
|||
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description,
|
||||
)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, priv1, priv2)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, true, priv1, priv2)
|
||||
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)})
|
||||
mApp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
|
@ -161,7 +161,7 @@ func TestStakeMsgs(t *testing.T) {
|
|||
description = NewDescription("bar_moniker", "", "", "")
|
||||
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, true, priv1)
|
||||
validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true)
|
||||
require.Equal(t, description, validator.Description)
|
||||
|
||||
|
@ -169,13 +169,13 @@ func TestStakeMsgs(t *testing.T) {
|
|||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin})
|
||||
delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, priv2)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, true, priv2)
|
||||
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10))
|
||||
|
||||
// begin unbonding
|
||||
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, sdk.ValAddress(addr1), sdk.NewDec(10))
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, priv2)
|
||||
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, true, priv2)
|
||||
|
||||
// delegation should exist anymore
|
||||
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{})
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
@ -60,6 +61,7 @@ type EditDelegationsBody struct {
|
|||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
Delegations []msgDelegationsInput `json:"delegations"`
|
||||
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
|
||||
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
|
||||
|
@ -106,18 +108,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for _, msg := range m.Delegations {
|
||||
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -133,29 +135,29 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for _, msg := range m.BeginRedelegates {
|
||||
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
return
|
||||
}
|
||||
|
||||
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
shares, err := sdk.NewDecFromStr(msg.SharesAmount)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -172,24 +174,24 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for _, msg := range m.CompleteRedelegates {
|
||||
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -205,24 +207,24 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for _, msg := range m.BeginUnbondings {
|
||||
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
return
|
||||
}
|
||||
|
||||
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
shares, err := sdk.NewDecFromStr(msg.SharesAmount)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -238,18 +240,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for _, msg := range m.CompleteUnbondings {
|
||||
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -276,10 +278,20 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
|
||||
m.Sequence++
|
||||
|
||||
if m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cliCtx = cliCtx.WithGasAdjustment(adjustment)
|
||||
|
||||
if utils.HasDryRunArg(r) || m.Gas == 0 {
|
||||
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if utils.HasDryRunArg(r) {
|
||||
utils.WriteSimulationResponse(w, txCtx.Gas)
|
||||
return
|
||||
}
|
||||
txCtx = newCtx
|
||||
|
@ -287,7 +299,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
|
||||
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -301,7 +313,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
for i, txBytes := range signedTxs {
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -310,7 +322,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
|
|||
|
||||
output, err := wire.MarshalJSONIndent(cdc, results[:])
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
|
||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -128,17 +129,19 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
|
|||
}
|
||||
|
||||
func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result {
|
||||
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoValidatorFound(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
if msg.Delegation.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadDenom(k.Codespace()).Result()
|
||||
}
|
||||
if validator.Jailed {
|
||||
|
||||
if validator.Jailed && !bytes.Equal(validator.Operator, msg.DelegatorAddr) {
|
||||
return ErrValidatorJailed(k.Codespace()).Result()
|
||||
}
|
||||
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
|
@ -149,6 +152,7 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
|
|||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
|
||||
)
|
||||
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
|
|
|
@ -85,8 +85,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
|||
keeper.Jail(ctx, keep.PKs[0])
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status) // ensure is unbonded
|
||||
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure is unbonded
|
||||
require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
|
||||
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
|
||||
|
||||
// the old power record should have been deleted as the power changed
|
||||
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||
|
@ -193,6 +193,97 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
|
|||
require.False(t, got.IsOK(), "%v", got)
|
||||
}
|
||||
|
||||
func TestLegacyValidatorDelegations(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, int64(1000))
|
||||
setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
bondAmount := int64(10)
|
||||
valAddr, valPubKey := sdk.ValAddress(keep.Addrs[0]), keep.PKs[0]
|
||||
delAddr := keep.Addrs[1]
|
||||
|
||||
// create validator
|
||||
msgCreateVal := newTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
|
||||
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
||||
|
||||
// verify the validator exists and has the correct attributes
|
||||
validator, found := keeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Bonded, validator.Status)
|
||||
require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64())
|
||||
require.Equal(t, bondAmount, validator.BondedTokens().RoundInt64())
|
||||
|
||||
// delegate tokens to the validator
|
||||
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
// verify validator bonded shares
|
||||
validator, found = keeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bondAmount*2, validator.DelegatorShares.RoundInt64())
|
||||
require.Equal(t, bondAmount*2, validator.BondedTokens().RoundInt64())
|
||||
|
||||
// unbond validator total self-delegations (which should jail the validator)
|
||||
unbondShares := sdk.NewDec(10)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(valAddr), valAddr, unbondShares)
|
||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(sdk.AccAddress(valAddr), valAddr)
|
||||
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got)
|
||||
|
||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got %v", got)
|
||||
|
||||
// verify the validator record still exists, is jailed, and has correct tokens
|
||||
validator, found = keeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.True(t, validator.Jailed)
|
||||
require.Equal(t, sdk.NewDec(10), validator.Tokens)
|
||||
|
||||
// verify delegation still exists
|
||||
bond, found := keeper.GetDelegation(ctx, delAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bondAmount, bond.Shares.RoundInt64())
|
||||
require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64())
|
||||
|
||||
// verify a delegator cannot create a new delegation to the now jailed validator
|
||||
msgDelegate = newTestMsgDelegate(delAddr, valAddr, bondAmount)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.False(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
|
||||
|
||||
// verify the validator can still self-delegate
|
||||
msgSelfDelegate := newTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
|
||||
got = handleMsgDelegate(ctx, msgSelfDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
|
||||
|
||||
// verify validator bonded shares
|
||||
validator, found = keeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bondAmount*2, validator.DelegatorShares.RoundInt64())
|
||||
require.Equal(t, bondAmount*2, validator.Tokens.RoundInt64())
|
||||
|
||||
// unjail the validator now that is has non-zero self-delegated shares
|
||||
keeper.Unjail(ctx, valPubKey)
|
||||
|
||||
// verify the validator can now accept delegations
|
||||
msgDelegate = newTestMsgDelegate(delAddr, valAddr, bondAmount)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
// verify validator bonded shares
|
||||
validator, found = keeper.GetValidator(ctx, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bondAmount*3, validator.DelegatorShares.RoundInt64())
|
||||
require.Equal(t, bondAmount*3, validator.Tokens.RoundInt64())
|
||||
|
||||
// verify new delegation
|
||||
bond, found = keeper.GetDelegation(ctx, delAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bondAmount*2, bond.Shares.RoundInt64())
|
||||
require.Equal(t, bondAmount*3, validator.DelegatorShares.RoundInt64())
|
||||
}
|
||||
|
||||
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
|
|
|
@ -2,6 +2,7 @@ package keeper
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
@ -288,6 +289,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
|||
if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Jailed == false {
|
||||
validator.Jailed = true
|
||||
}
|
||||
|
||||
k.RemoveDelegation(ctx, delegation)
|
||||
} else {
|
||||
// Update height
|
||||
|
@ -307,11 +309,37 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
|||
k.RemoveValidator(ctx, validator.Operator)
|
||||
}
|
||||
|
||||
return
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
// get info for begin functions: MinTime and CreationHeight
|
||||
func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sdk.ValAddress) (
|
||||
minTime time.Time, height int64, completeNow bool) {
|
||||
|
||||
validator, found := k.GetValidator(ctx, valSrcAddr)
|
||||
switch {
|
||||
case !found || validator.Status == sdk.Bonded:
|
||||
|
||||
// the longest wait - just unbonding period from now
|
||||
minTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
height = ctx.BlockHeader().Height
|
||||
return minTime, height, false
|
||||
|
||||
case validator.IsUnbonded(ctx):
|
||||
return minTime, height, true
|
||||
|
||||
case validator.Status == sdk.Unbonding:
|
||||
minTime = validator.UnbondingMinTime
|
||||
height = validator.UnbondingHeight
|
||||
return minTime, height, false
|
||||
|
||||
default:
|
||||
panic("unknown validator status")
|
||||
}
|
||||
}
|
||||
|
||||
// complete unbonding an unbonding record
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
||||
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
|
||||
|
@ -329,12 +357,22 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
|||
|
||||
// create the unbonding delegation
|
||||
params := k.GetParams(ctx)
|
||||
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr)
|
||||
balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
|
||||
|
||||
// no need to create the ubd object just complete now
|
||||
if completeNow {
|
||||
_, _, err := k.coinKeeper.AddCoins(ctx, delAddr, sdk.Coins{balance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorAddr: valAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
Balance: balance,
|
||||
InitialBalance: balance,
|
||||
|
@ -391,12 +429,17 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
|||
}
|
||||
|
||||
// create the unbonding delegation
|
||||
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, params, valSrcAddr)
|
||||
|
||||
if completeNow { // no need to create the redelegation object
|
||||
return nil
|
||||
}
|
||||
|
||||
red := types.Redelegation{
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorSrcAddr: valSrcAddr,
|
||||
ValidatorDstAddr: valDstAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
SharesDst: sharesCreated,
|
||||
SharesSrc: sharesAmount,
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -162,9 +163,7 @@ func TestUnbondDelegation(t *testing.T) {
|
|||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
var err error
|
||||
var amount sdk.Dec
|
||||
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
|
||||
amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation
|
||||
|
||||
|
@ -180,6 +179,190 @@ func TestUnbondDelegation(t *testing.T) {
|
|||
require.Equal(t, int64(4), pool.BondedTokens.RoundInt64())
|
||||
}
|
||||
|
||||
// test removing all self delegation from a validator which should
|
||||
// shift it from the bonded to unbonded state
|
||||
func TestUndelegateSelfDelegation(t *testing.T) {
|
||||
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(20)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
||||
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||
}
|
||||
|
||||
func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(20)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
header := ctx.BlockHeader()
|
||||
blockHeight := int64(10)
|
||||
header.Height = blockHeight
|
||||
blockTime := time.Unix(333, 0)
|
||||
header.Time = blockTime
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond the all self-delegation to put validator in unbonding state
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||
params := keeper.GetParams(ctx)
|
||||
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
|
||||
|
||||
//change the context
|
||||
header = ctx.BlockHeader()
|
||||
blockHeight2 := int64(20)
|
||||
header.Height = blockHeight2
|
||||
blockTime2 := time.Unix(444, 0)
|
||||
header.Time = blockTime2
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond some of the other delegation's shares
|
||||
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
|
||||
require.NoError(t, err)
|
||||
|
||||
// retrieve the unbonding delegation
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.True(t, ubd.Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.MinTime))
|
||||
}
|
||||
|
||||
func TestUndelegateFromUnbondedValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(20)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: val0AccAddr,
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
header := ctx.BlockHeader()
|
||||
blockHeight := int64(10)
|
||||
header.Height = blockHeight
|
||||
blockTime := time.Unix(333, 0)
|
||||
header.Time = blockTime
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond the all self-delegation to put validator in unbonding state
|
||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||
params := keeper.GetParams(ctx)
|
||||
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
|
||||
|
||||
// change the context to one which makes the validator considered unbonded
|
||||
header = ctx.BlockHeader()
|
||||
blockHeight2 := int64(20)
|
||||
header.Height = blockHeight2
|
||||
blockTime2 := time.Unix(444, 0).Add(params.UnbondingTime)
|
||||
header.Time = blockTime2
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond some of the other delegation's shares
|
||||
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
|
||||
require.NoError(t, err)
|
||||
|
||||
// no ubd should have been found, coins should have been returned direcly to account
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.False(t, found, "%v", ubd)
|
||||
}
|
||||
|
||||
// Make sure that that the retrieving the delegations doesn't affect the state
|
||||
func TestGetRedelegationsFromValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
@ -259,3 +442,206 @@ func TestRedelegation(t *testing.T) {
|
|||
_, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestRedelegateSelfDelegation(t *testing.T) {
|
||||
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(30)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: val0AccAddr,
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second validator
|
||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
||||
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||
}
|
||||
|
||||
func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(30)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: val0AccAddr,
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
// create a second validator
|
||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
||||
|
||||
header := ctx.BlockHeader()
|
||||
blockHeight := int64(10)
|
||||
header.Height = blockHeight
|
||||
blockTime := time.Unix(333, 0)
|
||||
header.Time = blockTime
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond the all self-delegation to put validator in unbonding state
|
||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||
params := keeper.GetParams(ctx)
|
||||
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
|
||||
|
||||
//change the context
|
||||
header = ctx.BlockHeader()
|
||||
blockHeight2 := int64(20)
|
||||
header.Height = blockHeight2
|
||||
blockTime2 := time.Unix(444, 0)
|
||||
header.Time = blockTime2
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond some of the other delegation's shares
|
||||
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
|
||||
require.NoError(t, err)
|
||||
|
||||
// retrieve the unbonding delegation
|
||||
ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.True(t, ubd.Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.MinTime))
|
||||
}
|
||||
|
||||
func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.LooseTokens = sdk.NewDec(30)
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
selfDelegation := types.Delegation{
|
||||
DelegatorAddr: val0AccAddr,
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, selfDelegation)
|
||||
|
||||
// create a second delegation to this validator
|
||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = keeper.UpdateValidator(ctx, validator)
|
||||
pool = keeper.GetPool(ctx)
|
||||
delegation := types.Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: issuedShares,
|
||||
}
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
// create a second validator
|
||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
||||
|
||||
header := ctx.BlockHeader()
|
||||
blockHeight := int64(10)
|
||||
header.Height = blockHeight
|
||||
blockTime := time.Unix(333, 0)
|
||||
header.Time = blockTime
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond the all self-delegation to put validator in unbonding state
|
||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||
require.NoError(t, err)
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||
params := keeper.GetParams(ctx)
|
||||
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
|
||||
|
||||
// change the context to one which makes the validator considered unbonded
|
||||
header = ctx.BlockHeader()
|
||||
blockHeight2 := int64(20)
|
||||
header.Height = blockHeight2
|
||||
blockTime2 := time.Unix(444, 0).Add(params.UnbondingTime)
|
||||
header.Time = blockTime2
|
||||
ctx = ctx.WithBlockHeader(header)
|
||||
|
||||
// unbond some of the other delegation's shares
|
||||
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
|
||||
require.NoError(t, err)
|
||||
|
||||
// no ubd should have been found, coins should have been returned direcly to account
|
||||
ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.False(t, found, "%v", ubd)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ type Keeper struct {
|
|||
storeKey sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
coinKeeper bank.Keeper
|
||||
validatorHooks sdk.ValidatorHooks
|
||||
|
||||
// codespace
|
||||
codespace sdk.CodespaceType
|
||||
|
@ -23,11 +24,21 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.
|
|||
storeKey: key,
|
||||
cdc: cdc,
|
||||
coinKeeper: ck,
|
||||
validatorHooks: nil,
|
||||
codespace: codespace,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
// Set the validator hooks
|
||||
func (k Keeper) WithValidatorHooks(v sdk.ValidatorHooks) Keeper {
|
||||
if k.validatorHooks != nil {
|
||||
panic("cannot set validator hooks twice")
|
||||
}
|
||||
k.validatorHooks = v
|
||||
return k
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// return the codespace
|
||||
|
|
|
@ -91,6 +91,7 @@ func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.
|
|||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return bond
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,12 @@ import (
|
|||
// Infraction committed equal to or less than an unbonding period in the past,
|
||||
// so all unbonding delegations and redelegations from that height are stored
|
||||
// CONTRACT:
|
||||
// Slash will not slash unbonded validators (for the above reason)
|
||||
// CONTRACT:
|
||||
// Infraction committed at the current height or at a past height,
|
||||
// not at a height in the future
|
||||
//
|
||||
// nolint: gocyclo
|
||||
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) {
|
||||
logger := ctx.Logger().With("module", "x/stake")
|
||||
|
||||
|
@ -43,6 +47,12 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
|||
pubkey.Address()))
|
||||
return
|
||||
}
|
||||
|
||||
// should not be slashing unbonded
|
||||
if validator.IsUnbonded(ctx) {
|
||||
panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator))
|
||||
}
|
||||
|
||||
operatorAddress := validator.GetOperator()
|
||||
|
||||
// Track remaining slash amount for the validator
|
||||
|
@ -91,25 +101,24 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
|||
// Cannot decrease balance below zero
|
||||
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
|
||||
|
||||
// Get the current pool
|
||||
// burn validator's tokens
|
||||
pool := k.GetPool(ctx)
|
||||
// remove tokens from the validator
|
||||
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
|
||||
// burn tokens
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||
// update the pool
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
// update the validator, possibly kicking it out
|
||||
validator = k.UpdateValidator(ctx, validator)
|
||||
// remove validator if it has been reduced to zero shares
|
||||
|
||||
// remove validator if it has no more tokens
|
||||
if validator.Tokens.IsZero() {
|
||||
k.RemoveValidator(ctx, validator.Operator)
|
||||
}
|
||||
|
||||
// Log that a slash occurred!
|
||||
logger.Info(fmt.Sprintf(
|
||||
"Validator %s slashed by slashFactor %v, burned %v tokens",
|
||||
pubkey.Address(), slashFactor, tokensToBurn))
|
||||
"Validator %s slashed by slashFactor %s, burned %v tokens",
|
||||
pubkey.Address(), slashFactor.String(), tokensToBurn))
|
||||
|
||||
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
||||
return
|
||||
|
@ -134,12 +143,12 @@ func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) {
|
|||
}
|
||||
|
||||
// set the jailed flag on a validator
|
||||
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, jailed bool) {
|
||||
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) {
|
||||
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||
if !found {
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, jailed))
|
||||
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed))
|
||||
}
|
||||
validator.Jailed = jailed
|
||||
validator.Jailed = isJailed
|
||||
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
|
||||
return
|
||||
}
|
||||
|
@ -179,6 +188,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
|||
unbondingDelegation.Balance.Amount = unbondingDelegation.Balance.Amount.Sub(unbondingSlashAmount)
|
||||
k.SetUnbondingDelegation(ctx, unbondingDelegation)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
// Burn loose tokens
|
||||
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(slashAmount)
|
||||
|
@ -239,6 +249,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
|
|||
if err != nil {
|
||||
panic(fmt.Errorf("error unbonding delegator: %v", err))
|
||||
}
|
||||
|
||||
// Burn loose tokens
|
||||
pool := k.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// setup helper function
|
||||
// creates two validators
|
||||
// TODO integrate with test_common.go helper (CreateTestInput)
|
||||
// setup helper function - creates two validators
|
||||
func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
|
||||
// setup
|
||||
ctx, _, keeper := CreateTestInput(t, false, amt)
|
||||
|
@ -34,8 +34,11 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
|
|||
return ctx, keeper, params
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________
|
||||
|
||||
// tests Jail, Unjail
|
||||
func TestRevocation(t *testing.T) {
|
||||
|
||||
// setup
|
||||
ctx, keeper, _ := setupHelper(t, 10)
|
||||
addr := addrVals[0]
|
||||
|
@ -57,7 +60,6 @@ func TestRevocation(t *testing.T) {
|
|||
val, found = keeper.GetValidator(ctx, addr)
|
||||
require.True(t, found)
|
||||
require.False(t, val.GetJailed())
|
||||
|
||||
}
|
||||
|
||||
// tests slashUnbondingDelegation
|
||||
|
@ -95,8 +97,10 @@ func TestSlashUnbondingDelegation(t *testing.T) {
|
|||
require.Equal(t, int64(5), slashAmount.RoundInt64())
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
|
||||
// initialbalance unchanged
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), ubd.InitialBalance)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Balance)
|
||||
newPool := keeper.GetPool(ctx)
|
||||
|
@ -155,14 +159,18 @@ func TestSlashRedelegation(t *testing.T) {
|
|||
require.Equal(t, int64(5), slashAmount.RoundInt64())
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
|
||||
// initialbalance unchanged
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), rd.Balance)
|
||||
|
||||
// shares decreased
|
||||
del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(5), del.Shares.RoundInt64())
|
||||
|
||||
// pool bonded tokens decreased
|
||||
newPool := keeper.GetPool(ctx)
|
||||
require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||
|
@ -177,7 +185,7 @@ func TestSlashAtFutureHeight(t *testing.T) {
|
|||
}
|
||||
|
||||
// tests Slash at the current height
|
||||
func TestSlashAtCurrentHeight(t *testing.T) {
|
||||
func TestSlashValidatorAtCurrentHeight(t *testing.T) {
|
||||
ctx, keeper, _ := setupHelper(t, 10)
|
||||
pk := PKs[0]
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
|
|
@ -2,6 +2,7 @@ package keeper
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
@ -11,6 +12,19 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
)
|
||||
|
||||
// Cache the amino decoding of validators, as it can be the case that repeated slashing calls
|
||||
// cause many calls to GetValidator, which were shown to throttle the state machine in our
|
||||
// simulation. Note this is quite biased though, as the simulator does more slashes than a
|
||||
// live chain should, however we require the slashing to be fast as noone pays gas for it.
|
||||
type cachedValidator struct {
|
||||
val types.Validator
|
||||
marshalled string // marshalled amino bytes for the validator object (not operator address)
|
||||
}
|
||||
|
||||
// validatorCache-key: validator amino bytes
|
||||
var validatorCache = make(map[string]cachedValidator, 500)
|
||||
var validatorCacheList = list.New()
|
||||
|
||||
// get a single validator
|
||||
func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator types.Validator, found bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
@ -18,6 +32,28 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty
|
|||
if value == nil {
|
||||
return validator, false
|
||||
}
|
||||
|
||||
// If these amino encoded bytes are in the cache, return the cached validator
|
||||
strValue := string(value)
|
||||
if val, ok := validatorCache[strValue]; ok {
|
||||
valToReturn := val.val
|
||||
// Doesn't mutate the cache's value
|
||||
valToReturn.Operator = addr
|
||||
return valToReturn, true
|
||||
}
|
||||
|
||||
// amino bytes weren't found in cache, so amino unmarshal and add it to the cache
|
||||
validator = types.MustUnmarshalValidator(k.cdc, addr, value)
|
||||
cachedVal := cachedValidator{validator, strValue}
|
||||
validatorCache[strValue] = cachedValidator{validator, strValue}
|
||||
validatorCacheList.PushBack(cachedVal)
|
||||
|
||||
// if the cache is too big, pop off the last element from it
|
||||
if validatorCacheList.Len() > 500 {
|
||||
valToRemove := validatorCacheList.Remove(validatorCacheList.Front()).(cachedValidator)
|
||||
delete(validatorCache, valToRemove.marshalled)
|
||||
}
|
||||
|
||||
validator = types.MustUnmarshalValidator(k.cdc, addr, value)
|
||||
return validator, true
|
||||
}
|
||||
|
@ -212,6 +248,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
|||
cliffPower := k.GetCliffValidatorPower(ctx)
|
||||
|
||||
switch {
|
||||
|
||||
// if the validator is already bonded and the power is increasing, we need
|
||||
// perform the following:
|
||||
// a) update Tendermint
|
||||
|
@ -240,10 +277,11 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
|||
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
||||
// skip to completion
|
||||
|
||||
default:
|
||||
// default case - validator was either:
|
||||
// a) not-bonded and now has power-rank greater than cliff validator
|
||||
// b) bonded and now has decreased in power
|
||||
default:
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
|
||||
if updated {
|
||||
|
@ -307,10 +345,13 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
|
|||
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
|
||||
|
||||
if bytes.Equal(affectedVal.Operator, newCliffVal.Operator) {
|
||||
|
||||
// The affected validator remains the cliff validator, however, since
|
||||
// the store does not contain the new power, update the new power rank.
|
||||
store.Set(ValidatorPowerCliffKey, affectedValRank)
|
||||
|
||||
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
|
||||
|
||||
// The affected validator no longer remains the cliff validator as it's
|
||||
// power is greater than the new cliff validator.
|
||||
k.setCliffValidator(ctx, newCliffVal, pool)
|
||||
|
@ -321,7 +362,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
|
|||
|
||||
func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
|
||||
if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded {
|
||||
newValidator = k.unbondValidator(ctx, newValidator)
|
||||
newValidator = k.beginUnbondingValidator(ctx, newValidator)
|
||||
|
||||
// need to also clear the cliff validator spot because the jail has
|
||||
// opened up a new spot which will be filled when
|
||||
|
@ -416,8 +457,12 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
}
|
||||
|
||||
// if we've reached jailed validators no further bonded validators exist
|
||||
if validator.Jailed {
|
||||
break
|
||||
}
|
||||
|
||||
// increment bondedValidatorsCount / get the validator to bond
|
||||
if !validator.Jailed {
|
||||
if validator.Status != sdk.Bonded {
|
||||
validatorToBond = validator
|
||||
if newValidatorBonded {
|
||||
|
@ -425,10 +470,6 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
newValidatorBonded = true
|
||||
}
|
||||
} else {
|
||||
// TODO: document why we must break here.
|
||||
break
|
||||
}
|
||||
|
||||
// increment the total number of bonded validators and potentially mark
|
||||
// the validator to bond
|
||||
|
@ -464,13 +505,15 @@ func (k Keeper) UpdateBondedValidators(
|
|||
}
|
||||
|
||||
if bytes.Equal(validatorToBond.Operator, affectedValidator.Operator) {
|
||||
// unbond the old cliff validator iff the affected validator was
|
||||
// newly bonded and has greater power
|
||||
k.unbondValidator(ctx, oldCliffVal)
|
||||
|
||||
// begin unbonding the old cliff validator iff the affected
|
||||
// validator was newly bonded and has greater power
|
||||
k.beginUnbondingValidator(ctx, oldCliffVal)
|
||||
} else {
|
||||
// otherwise unbond the affected validator, which must have been
|
||||
// kicked out
|
||||
affectedValidator = k.unbondValidator(ctx, affectedValidator)
|
||||
|
||||
// otherwise begin unbonding the affected validator, which must
|
||||
// have been kicked out
|
||||
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,25 +606,30 @@ func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
|
|||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
k.beginUnbondingValidator(ctx, validator)
|
||||
}
|
||||
}
|
||||
|
||||
// perform all the store operations for when a validator status becomes unbonded
|
||||
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
|
||||
// sanity check
|
||||
if validator.Status == sdk.Unbonded {
|
||||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
||||
if validator.Status == sdk.Unbonded ||
|
||||
validator.Status == sdk.Unbonding {
|
||||
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
|
||||
}
|
||||
|
||||
// set the status
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
|
||||
k.SetPool(ctx, pool)
|
||||
|
||||
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
|
||||
validator.UnbondingHeight = ctx.BlockHeader().Height
|
||||
|
||||
// save the now unbonded validator record
|
||||
k.SetValidator(ctx, validator)
|
||||
|
||||
|
@ -591,6 +639,13 @@ func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) type
|
|||
|
||||
// also remove from the Bonded types.Validators Store
|
||||
store.Delete(GetValidatorsBondedIndexKey(validator.Operator))
|
||||
|
||||
// call the unbond hook if present
|
||||
if k.validatorHooks != nil {
|
||||
k.validatorHooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress())
|
||||
}
|
||||
|
||||
// return updated validator
|
||||
return validator
|
||||
}
|
||||
|
||||
|
@ -617,6 +672,12 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
|
|||
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||
store.Set(GetTendermintUpdatesKey(validator.Operator), bzABCI)
|
||||
|
||||
// call the bond hook if present
|
||||
if k.validatorHooks != nil {
|
||||
k.validatorHooks.OnValidatorBonded(ctx, validator.ConsAddress())
|
||||
}
|
||||
|
||||
// return updated validator
|
||||
return validator
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
|
|||
|
||||
expectedValStatus := map[int]sdk.BondStatus{
|
||||
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
|
||||
0: sdk.Unbonded, 1: sdk.Unbonded, 2: sdk.Unbonded, 3: sdk.Unbonded, 6: sdk.Unbonded,
|
||||
0: sdk.Unbonding, 1: sdk.Unbonding, 2: sdk.Unbonding, 3: sdk.Unbonding, 6: sdk.Unbonding,
|
||||
}
|
||||
|
||||
// require all the validators have their respective statuses
|
||||
|
@ -145,9 +145,11 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
|
|||
valAddr := validators[valIdx].Operator
|
||||
val, _ := keeper.GetValidator(ctx, valAddr)
|
||||
|
||||
require.Equal(
|
||||
t, val.GetStatus(), status,
|
||||
fmt.Sprintf("expected validator to have status: %s", sdk.BondStatusToString(status)))
|
||||
assert.Equal(
|
||||
t, status, val.GetStatus(),
|
||||
fmt.Sprintf("expected validator at index %v to have status: %s",
|
||||
valIdx,
|
||||
sdk.BondStatusToString(status)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -610,8 +612,8 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
|
|||
validators[i], found = keeper.GetValidator(ctx, validators[i].Operator)
|
||||
require.True(t, found)
|
||||
}
|
||||
assert.Equal(t, sdk.Unbonded, validators[0].Status)
|
||||
assert.Equal(t, sdk.Unbonded, validators[1].Status)
|
||||
assert.Equal(t, sdk.Unbonding, validators[0].Status)
|
||||
assert.Equal(t, sdk.Unbonding, validators[1].Status)
|
||||
assert.Equal(t, sdk.Bonded, validators[2].Status)
|
||||
assert.Equal(t, sdk.Bonded, validators[3].Status)
|
||||
assert.Equal(t, sdk.Unbonded, validators[4].Status)
|
||||
|
|
|
@ -45,6 +45,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim
|
|||
case sdk.Bonded:
|
||||
bonded = bonded.Add(validator.GetPower())
|
||||
case sdk.Unbonding:
|
||||
loose = loose.Add(validator.GetTokens().RoundInt())
|
||||
case sdk.Unbonded:
|
||||
loose = loose.Add(validator.GetTokens().RoundInt())
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
@ -19,7 +17,9 @@ import (
|
|||
|
||||
// SimulateMsgCreateValidator
|
||||
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
description := stake.Description{
|
||||
Moniker: simulation.RandStringOfLength(r, 10),
|
||||
|
@ -41,9 +41,11 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
|
|||
PubKey: pubkey,
|
||||
Delegation: sdk.NewCoin(denom, amount),
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -56,7 +58,9 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
|
|||
|
||||
// SimulateMsgEditValidator
|
||||
func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
description := stake.Description{
|
||||
Moniker: simulation.RandStringOfLength(r, 10),
|
||||
Identity: simulation.RandStringOfLength(r, 10),
|
||||
|
@ -70,9 +74,11 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
|
|||
Description: description,
|
||||
ValidatorAddr: address,
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -84,7 +90,9 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
|
|||
|
||||
// SimulateMsgDelegate
|
||||
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
validatorKey := simulation.RandomKey(r, keys)
|
||||
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
|
||||
|
@ -102,9 +110,11 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat
|
|||
ValidatorAddr: validatorAddress,
|
||||
Delegation: sdk.NewCoin(denom, amount),
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -116,7 +126,9 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat
|
|||
|
||||
// SimulateMsgBeginUnbonding
|
||||
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
validatorKey := simulation.RandomKey(r, keys)
|
||||
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
|
||||
|
@ -134,9 +146,11 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
|
|||
ValidatorAddr: validatorAddress,
|
||||
SharesAmount: sdk.NewDecFromInt(amount),
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -148,7 +162,9 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
|
|||
|
||||
// SimulateMsgCompleteUnbonding
|
||||
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
validatorKey := simulation.RandomKey(r, keys)
|
||||
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
|
||||
delegatorKey := simulation.RandomKey(r, keys)
|
||||
|
@ -157,9 +173,11 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
|
|||
DelegatorAddr: delegatorAddress,
|
||||
ValidatorAddr: validatorAddress,
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -171,7 +189,9 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
|
|||
|
||||
// SimulateMsgBeginRedelegate
|
||||
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
sourceValidatorKey := simulation.RandomKey(r, keys)
|
||||
sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address())
|
||||
|
@ -193,9 +213,11 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation
|
|||
ValidatorDstAddr: destValidatorAddress,
|
||||
SharesAmount: sdk.NewDecFromInt(amount),
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
@ -207,7 +229,9 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation
|
|||
|
||||
// SimulateMsgCompleteRedelegate
|
||||
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
|
||||
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
handler := stake.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
|
||||
validatorSrcKey := simulation.RandomKey(r, keys)
|
||||
validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address())
|
||||
validatorDstKey := simulation.RandomKey(r, keys)
|
||||
|
@ -219,9 +243,11 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
|
|||
ValidatorSrcAddr: validatorSrcAddress,
|
||||
ValidatorDstAddr: validatorDstAddress,
|
||||
}
|
||||
require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
if msg.ValidateBasic() != nil {
|
||||
tb.Fatalf("expected msg to pass ValidateBasic: %s, log %s", msg.GetSignBytes(), log)
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := stake.NewHandler(k)(ctx, msg)
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
write()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package types
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
@ -31,15 +32,14 @@ type Validator struct {
|
|||
Description Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
|
||||
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
LastBondedTokens sdk.Dec `json:"prev_bonded_tokens"` // Previous bonded tokens held
|
||||
}
|
||||
|
||||
// NewValidator - initialize a new validator
|
||||
|
@ -54,12 +54,12 @@ func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Des
|
|||
Description: description,
|
||||
BondHeight: int64(0),
|
||||
BondIntraTxCounter: int16(0),
|
||||
ProposerRewardPool: sdk.Coins{},
|
||||
UnbondingHeight: int64(0),
|
||||
UnbondingMinTime: time.Unix(0, 0),
|
||||
Commission: sdk.ZeroDec(),
|
||||
CommissionMax: sdk.ZeroDec(),
|
||||
CommissionChangeRate: sdk.ZeroDec(),
|
||||
CommissionChangeToday: sdk.ZeroDec(),
|
||||
LastBondedTokens: sdk.ZeroDec(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,12 +73,12 @@ type validatorValue struct {
|
|||
Description Description
|
||||
BondHeight int64
|
||||
BondIntraTxCounter int16
|
||||
ProposerRewardPool sdk.Coins
|
||||
UnbondingHeight int64
|
||||
UnbondingMinTime time.Time
|
||||
Commission sdk.Dec
|
||||
CommissionMax sdk.Dec
|
||||
CommissionChangeRate sdk.Dec
|
||||
CommissionChangeToday sdk.Dec
|
||||
LastBondedTokens sdk.Dec
|
||||
}
|
||||
|
||||
// return the redelegation without fields contained within the key for the store
|
||||
|
@ -92,12 +92,12 @@ func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte {
|
|||
Description: validator.Description,
|
||||
BondHeight: validator.BondHeight,
|
||||
BondIntraTxCounter: validator.BondIntraTxCounter,
|
||||
ProposerRewardPool: validator.ProposerRewardPool,
|
||||
UnbondingHeight: validator.UnbondingHeight,
|
||||
UnbondingMinTime: validator.UnbondingMinTime,
|
||||
Commission: validator.Commission,
|
||||
CommissionMax: validator.CommissionMax,
|
||||
CommissionChangeRate: validator.CommissionChangeRate,
|
||||
CommissionChangeToday: validator.CommissionChangeToday,
|
||||
LastBondedTokens: validator.LastBondedTokens,
|
||||
}
|
||||
return cdc.MustMarshalBinary(val)
|
||||
}
|
||||
|
@ -108,7 +108,6 @@ func MustUnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) Validat
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
|
@ -134,12 +133,12 @@ func UnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) (validator
|
|||
Description: storeValue.Description,
|
||||
BondHeight: storeValue.BondHeight,
|
||||
BondIntraTxCounter: storeValue.BondIntraTxCounter,
|
||||
ProposerRewardPool: storeValue.ProposerRewardPool,
|
||||
UnbondingHeight: storeValue.UnbondingHeight,
|
||||
UnbondingMinTime: storeValue.UnbondingMinTime,
|
||||
Commission: storeValue.Commission,
|
||||
CommissionMax: storeValue.CommissionMax,
|
||||
CommissionChangeRate: storeValue.CommissionChangeRate,
|
||||
CommissionChangeToday: storeValue.CommissionChangeToday,
|
||||
LastBondedTokens: storeValue.LastBondedTokens,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -161,12 +160,12 @@ func (v Validator) HumanReadableString() (string, error) {
|
|||
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String())
|
||||
resp += fmt.Sprintf("Description: %s\n", v.Description)
|
||||
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
|
||||
resp += fmt.Sprintf("Proposer Reward Pool: %s\n", v.ProposerRewardPool.String())
|
||||
resp += fmt.Sprintf("Unbonding Height: %d\n", v.UnbondingHeight)
|
||||
resp += fmt.Sprintf("Minimum Unbonding Time: %v\n", v.UnbondingMinTime)
|
||||
resp += fmt.Sprintf("Commission: %s\n", v.Commission.String())
|
||||
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
|
||||
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
|
||||
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
|
||||
resp += fmt.Sprintf("Previous Bonded Tokens: %s\n", v.LastBondedTokens.String())
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -186,15 +185,14 @@ type BechValidator struct {
|
|||
Description Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
|
||||
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
LastBondedTokens sdk.Dec `json:"prev_bonded_shares"` // last bonded token amount
|
||||
}
|
||||
|
||||
// get the bech validator from the the regular validator
|
||||
|
@ -216,14 +214,13 @@ func (v Validator) Bech32Validator() (BechValidator, error) {
|
|||
Description: v.Description,
|
||||
BondHeight: v.BondHeight,
|
||||
BondIntraTxCounter: v.BondIntraTxCounter,
|
||||
ProposerRewardPool: v.ProposerRewardPool,
|
||||
UnbondingHeight: v.UnbondingHeight,
|
||||
UnbondingMinTime: v.UnbondingMinTime,
|
||||
|
||||
Commission: v.Commission,
|
||||
CommissionMax: v.CommissionMax,
|
||||
CommissionChangeRate: v.CommissionChangeRate,
|
||||
CommissionChangeToday: v.CommissionChangeToday,
|
||||
|
||||
LastBondedTokens: v.LastBondedTokens,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -238,12 +235,15 @@ func (v Validator) Equal(c2 Validator) bool {
|
|||
v.Tokens.Equal(c2.Tokens) &&
|
||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||
v.Description == c2.Description &&
|
||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||
v.Commission.Equal(c2.Commission) &&
|
||||
v.CommissionMax.Equal(c2.CommissionMax) &&
|
||||
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
|
||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
|
||||
v.LastBondedTokens.Equal(c2.LastBondedTokens)
|
||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday)
|
||||
}
|
||||
|
||||
// return the TM validator address
|
||||
func (v Validator) ConsAddress() sdk.ConsAddress {
|
||||
return sdk.ConsAddress(v.PubKey.Address())
|
||||
}
|
||||
|
||||
// constant used in flags to indicate that description field should not be updated
|
||||
|
@ -423,6 +423,20 @@ func (v Validator) BondedTokens() sdk.Dec {
|
|||
return sdk.ZeroDec()
|
||||
}
|
||||
|
||||
// Returns if the validator should be considered unbonded
|
||||
func (v Validator) IsUnbonded(ctx sdk.Context) bool {
|
||||
switch v.Status {
|
||||
case sdk.Unbonded:
|
||||
return true
|
||||
case sdk.Unbonding:
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if ctxTime.After(v.UnbondingMinTime) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
|
|
Loading…
Reference in New Issue