Merge branch 'develop' into cwgoes/nextvalset
This commit is contained in:
commit
8f4d3a6cd7
|
@ -3,7 +3,7 @@ version: 2
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
working_directory: /go/src/github.com/cosmos/cosmos-sdk
|
working_directory: /go/src/github.com/cosmos/cosmos-sdk
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.10.3
|
- image: circleci/golang:1.11.1
|
||||||
environment:
|
environment:
|
||||||
GOBIN: /tmp/workspace/bin
|
GOBIN: /tmp/workspace/bin
|
||||||
|
|
||||||
|
@ -206,6 +206,12 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: run localnet and exit on failure
|
name: run localnet and exit on failure
|
||||||
command: |
|
command: |
|
||||||
|
pushd /tmp
|
||||||
|
wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
|
||||||
|
sudo tar -xvf go1.11.linux-amd64.tar.gz
|
||||||
|
sudo rm -rf /usr/local/go
|
||||||
|
sudo mv go /usr/local
|
||||||
|
popd
|
||||||
set -x
|
set -x
|
||||||
make get_tools
|
make get_tools
|
||||||
make get_vendor_deps
|
make get_vendor_deps
|
||||||
|
|
|
@ -17,6 +17,7 @@ docs/_build
|
||||||
examples/basecoin/app/data
|
examples/basecoin/app/data
|
||||||
baseapp/data/*
|
baseapp/data/*
|
||||||
client/lcd/keys/*
|
client/lcd/keys/*
|
||||||
|
client/lcd/statik/statik.go
|
||||||
mytestnet
|
mytestnet
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
|
@ -239,12 +239,12 @@
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4"
|
digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3"
|
||||||
name = "github.com/mitchellh/mapstructure"
|
name = "github.com/mitchellh/mapstructure"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
|
revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad"
|
||||||
version = "v1.0.0"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||||
|
@ -313,6 +313,14 @@
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2"
|
revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64"
|
||||||
|
name = "github.com/rakyll/statik"
|
||||||
|
packages = ["fs"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "aa8a7b1baecd0f31a436bf7956fcdcc609a83035"
|
||||||
|
version = "v0.1.4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
|
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
|
||||||
name = "github.com/rcrowley/go-metrics"
|
name = "github.com/rcrowley/go-metrics"
|
||||||
|
@ -536,7 +544,7 @@
|
||||||
"salsa20/salsa",
|
"salsa20/salsa",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
|
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||||
|
@ -556,14 +564,14 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:68023dc297a659d5eb2dafd62eda811456b338c5b3ec3c27da79e8a47d3f456a"
|
digest = "1:8bc8ecef1d63576cfab4d08b44a1f255dd67e5b019b7a44837d62380f266a91c"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = [
|
packages = [
|
||||||
"cpu",
|
"cpu",
|
||||||
"unix",
|
"unix",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "2f1df4e56cdeb503a08d8577e6f1a7eb12efab82"
|
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||||
|
@ -590,11 +598,11 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
|
digest = "1:1e6b0176e8c5dd8ff551af65c76f8b73a99bcf4d812cedff1b91711b7df4804c"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "0e822944c569bf5c9afd034adaa56208bd2906ac"
|
revision = "c7e5094acea1ca1b899e2259d80a6b0f882f81f8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||||
|
@ -651,6 +659,7 @@
|
||||||
"github.com/mitchellh/go-homedir",
|
"github.com/mitchellh/go-homedir",
|
||||||
"github.com/pelletier/go-toml",
|
"github.com/pelletier/go-toml",
|
||||||
"github.com/pkg/errors",
|
"github.com/pkg/errors",
|
||||||
|
"github.com/rakyll/statik/fs",
|
||||||
"github.com/spf13/cobra",
|
"github.com/spf13/cobra",
|
||||||
"github.com/spf13/pflag",
|
"github.com/spf13/pflag",
|
||||||
"github.com/spf13/viper",
|
"github.com/spf13/viper",
|
||||||
|
|
|
@ -67,6 +67,10 @@
|
||||||
name = "github.com/zondax/ledger-goclient"
|
name = "github.com/zondax/ledger-goclient"
|
||||||
version = "=v0.1.0"
|
version = "=v0.1.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/rakyll/statik"
|
||||||
|
version = "=v0.1.4"
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
go-tests = true
|
go-tests = true
|
||||||
unused-packages = true
|
unused-packages = true
|
||||||
|
|
13
Makefile
13
Makefile
|
@ -32,7 +32,7 @@ TMP_BUILD_TAGS := $(BUILD_TAGS)
|
||||||
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
|
BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
build: check-ledger
|
build: check-ledger update_gaia_lite_docs
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
||||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
||||||
|
@ -44,6 +44,9 @@ endif
|
||||||
build-linux:
|
build-linux:
|
||||||
LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build
|
LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build
|
||||||
|
|
||||||
|
update_gaia_lite_docs:
|
||||||
|
@statik -src=client/lcd/swagger-ui -dest=client/lcd -f
|
||||||
|
|
||||||
build_cosmos-sdk-cli:
|
build_cosmos-sdk-cli:
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli
|
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli
|
||||||
|
@ -64,7 +67,7 @@ else
|
||||||
go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli
|
go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli
|
||||||
endif
|
endif
|
||||||
|
|
||||||
install: check-ledger
|
install: check-ledger update_gaia_lite_docs
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
||||||
|
|
||||||
|
@ -179,14 +182,14 @@ test_cover:
|
||||||
|
|
||||||
test_lint:
|
test_lint:
|
||||||
gometalinter.v2 --config=tools/gometalinter.json ./...
|
gometalinter.v2 --config=tools/gometalinter.json ./...
|
||||||
!(gometalinter.v2 --exclude /usr/lib/go/src/ --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
|
!(gometalinter.v2 --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
|
||||||
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
|
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
|
||||||
dep status >> /dev/null
|
dep status >> /dev/null
|
||||||
!(grep -n branch Gopkg.toml)
|
!(grep -n branch Gopkg.toml)
|
||||||
|
|
||||||
format:
|
format:
|
||||||
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -w -s
|
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s
|
||||||
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w
|
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
@go test -bench=. $(PACKAGES_NOSIMULATION)
|
@go test -bench=. $(PACKAGES_NOSIMULATION)
|
||||||
|
|
|
@ -39,6 +39,7 @@ BREAKING CHANGES
|
||||||
* `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub`
|
* `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub`
|
||||||
* `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub`
|
* `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub`
|
||||||
* [x/stake] [#1013] TendermintUpdates now uses transient store
|
* [x/stake] [#1013] TendermintUpdates now uses transient store
|
||||||
|
* [x/stake] \#2435 Remove empty bytes from the ValidatorPowerRank store key
|
||||||
* [x/gov] [#2195] Governance uses BFT Time
|
* [x/gov] [#2195] Governance uses BFT Time
|
||||||
* [x/gov] \#2256 Removed slashing for governance non-voting validators
|
* [x/gov] \#2256 Removed slashing for governance non-voting validators
|
||||||
* [simulation] \#2162 Added back correct supply invariants
|
* [simulation] \#2162 Added back correct supply invariants
|
||||||
|
@ -66,6 +67,7 @@ BREAKING CHANGES
|
||||||
* [x/auth] \#2377 auth.StdSignMsg -> txbuilder.StdSignMsg
|
* [x/auth] \#2377 auth.StdSignMsg -> txbuilder.StdSignMsg
|
||||||
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
|
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
|
||||||
* [x/staking] \#2236 more distribution hooks for distribution
|
* [x/staking] \#2236 more distribution hooks for distribution
|
||||||
|
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
|
||||||
|
|
||||||
* Tendermint
|
* Tendermint
|
||||||
|
|
||||||
|
@ -77,6 +79,7 @@ FEATURES
|
||||||
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
||||||
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
||||||
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
||||||
|
* [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers`
|
||||||
|
|
||||||
* Gaia CLI (`gaiacli`)
|
* Gaia CLI (`gaiacli`)
|
||||||
* [cli] Cmds to query staking pool and params
|
* [cli] Cmds to query staking pool and params
|
||||||
|
@ -110,6 +113,7 @@ FEATURES
|
||||||
* [x/auth] \#2376 Remove FeePayer() from StdTx
|
* [x/auth] \#2376 Remove FeePayer() from StdTx
|
||||||
* [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement
|
* [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement
|
||||||
basis for the validator commission model.
|
basis for the validator commission model.
|
||||||
|
* [x/auth] Support account removal in the account mapper.
|
||||||
|
|
||||||
* Tendermint
|
* Tendermint
|
||||||
|
|
||||||
|
@ -130,6 +134,7 @@ IMPROVEMENTS
|
||||||
* [x/auth] Signature verification's gas cost now accounts for pubkey type. [#2046](https://github.com/tendermint/tendermint/pull/2046)
|
* [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] [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)
|
* [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200)
|
||||||
|
* [x/stake] \#2435 Improve memory efficiency of getting the various store keys
|
||||||
* [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state.
|
* [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state.
|
||||||
* Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571
|
* Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571
|
||||||
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
||||||
|
@ -175,5 +180,7 @@ BUG FIXES
|
||||||
* [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker`
|
* [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker`
|
||||||
* [simulation] \#1924 Make simulation stop on SIGTERM
|
* [simulation] \#1924 Make simulation stop on SIGTERM
|
||||||
* [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository.
|
* [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository.
|
||||||
|
* [\#2416](https://github.com/cosmos/cosmos-sdk/issues/2416) Refactored
|
||||||
|
`InitializeTestLCD` to properly include proposing validator in genesis state.
|
||||||
|
|
||||||
* Tendermint
|
* Tendermint
|
||||||
|
|
|
@ -22,7 +22,7 @@ breaking changes.
|
||||||
## Gaia Testnet
|
## Gaia Testnet
|
||||||
|
|
||||||
To join the latest testnet, follow
|
To join the latest testnet, follow
|
||||||
[the guide](https://cosmos.network/docs/getting-started/full-node.html#setting-up-a-new-node).
|
[the guide](./docs/getting-started/join-testnet.md).
|
||||||
|
|
||||||
For status updates and genesis files, see the
|
For status updates and genesis files, see the
|
||||||
[testnets repo](https://github.com/cosmos/testnets).
|
[testnets repo](https://github.com/cosmos/testnets).
|
||||||
|
@ -30,15 +30,14 @@ For status updates and genesis files, see the
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
See the
|
See the
|
||||||
[install instructions](https://cosmos.network/docs/getting-started/installation.html).
|
[install instructions](./docs/getting-started/installation.md).
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
See the [Cosmos Docs](https://cosmos.network/docs/)
|
See the [Cosmos Docs](https://cosmos.network/docs/)
|
||||||
|
|
||||||
- [Getting started with the SDK](https://cosmos.network/docs/sdk/core/intro.html)
|
- [Getting started with the SDK](./docs/sdk/core/intro.md)
|
||||||
- [SDK Examples](/examples)
|
- [SDK Examples](/examples)
|
||||||
- [Join the testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node)
|
|
||||||
|
|
||||||
## Disambiguation
|
## Disambiguation
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,22 @@ func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (*ctypes.ResultBroadc
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BroadcastTxSync broadcasts transaction bytes to a Tendermint node
|
||||||
|
// synchronously.
|
||||||
|
func (ctx CLIContext) BroadcastTxSync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
|
||||||
|
node, err := ctx.GetNode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := node.BroadcastTxSync(tx)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node
|
// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node
|
||||||
// asynchronously.
|
// asynchronously.
|
||||||
func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
|
func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ type CLIContext struct {
|
||||||
GenerateOnly bool
|
GenerateOnly bool
|
||||||
fromAddress types.AccAddress
|
fromAddress types.AccAddress
|
||||||
fromName string
|
fromName string
|
||||||
|
Indent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
||||||
|
@ -87,6 +88,7 @@ func NewCLIContext() CLIContext {
|
||||||
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
||||||
fromAddress: fromAddress,
|
fromAddress: fromAddress,
|
||||||
fromName: fromName,
|
fromName: fromName,
|
||||||
|
Indent: viper.GetBool(client.FlagIndentResponse),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,24 +17,25 @@ const (
|
||||||
DefaultGasLimit = 200000
|
DefaultGasLimit = 200000
|
||||||
GasFlagSimulate = "simulate"
|
GasFlagSimulate = "simulate"
|
||||||
|
|
||||||
FlagUseLedger = "ledger"
|
FlagUseLedger = "ledger"
|
||||||
FlagChainID = "chain-id"
|
FlagChainID = "chain-id"
|
||||||
FlagNode = "node"
|
FlagNode = "node"
|
||||||
FlagHeight = "height"
|
FlagHeight = "height"
|
||||||
FlagGas = "gas"
|
FlagGas = "gas"
|
||||||
FlagGasAdjustment = "gas-adjustment"
|
FlagGasAdjustment = "gas-adjustment"
|
||||||
FlagTrustNode = "trust-node"
|
FlagTrustNode = "trust-node"
|
||||||
FlagFrom = "from"
|
FlagFrom = "from"
|
||||||
FlagName = "name"
|
FlagName = "name"
|
||||||
FlagAccountNumber = "account-number"
|
FlagAccountNumber = "account-number"
|
||||||
FlagSequence = "sequence"
|
FlagSequence = "sequence"
|
||||||
FlagMemo = "memo"
|
FlagMemo = "memo"
|
||||||
FlagFee = "fee"
|
FlagFee = "fee"
|
||||||
FlagAsync = "async"
|
FlagAsync = "async"
|
||||||
FlagJson = "json"
|
FlagJson = "json"
|
||||||
FlagPrintResponse = "print-response"
|
FlagPrintResponse = "print-response"
|
||||||
FlagDryRun = "dry-run"
|
FlagDryRun = "dry-run"
|
||||||
FlagGenerateOnly = "generate-only"
|
FlagGenerateOnly = "generate-only"
|
||||||
|
FlagIndentResponse = "indent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LineBreak can be included in a command list to provide a blank line
|
// LineBreak can be included in a command list to provide a blank line
|
||||||
|
@ -47,6 +48,7 @@ var (
|
||||||
// GetCommands adds common flags to query commands
|
// GetCommands adds common flags to query commands
|
||||||
func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
for _, c := range cmds {
|
for _, c := range cmds {
|
||||||
|
c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response")
|
||||||
c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||||
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
||||||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||||
|
@ -63,6 +65,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
// PostCommands adds common flags for commands to post tx
|
// PostCommands adds common flags for commands to post tx
|
||||||
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
for _, c := range cmds {
|
for _, c := range cmds {
|
||||||
|
c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response")
|
||||||
c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign")
|
c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign")
|
||||||
c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx")
|
c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx")
|
||||||
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
|
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
|
||||||
|
|
|
@ -42,7 +42,7 @@ func init() {
|
||||||
func TestKeys(t *testing.T) {
|
func TestKeys(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// get seed
|
// get seed
|
||||||
|
@ -125,7 +125,7 @@ func TestVersion(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// node info
|
// node info
|
||||||
|
@ -148,7 +148,7 @@ func TestVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeStatus(t *testing.T) {
|
func TestNodeStatus(t *testing.T) {
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// node info
|
// node info
|
||||||
|
@ -170,7 +170,7 @@ func TestNodeStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlock(t *testing.T) {
|
func TestBlock(t *testing.T) {
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
var resultBlock ctypes.ResultBlock
|
var resultBlock ctypes.ResultBlock
|
||||||
|
@ -200,7 +200,7 @@ func TestBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidators(t *testing.T) {
|
func TestValidators(t *testing.T) {
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
var resultVals rpc.ResultValidatorsOutput
|
var resultVals rpc.ResultValidatorsOutput
|
||||||
|
@ -235,7 +235,7 @@ func TestValidators(t *testing.T) {
|
||||||
func TestCoinSend(t *testing.T) {
|
func TestCoinSend(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
||||||
|
@ -303,7 +303,7 @@ func TestCoinSend(t *testing.T) {
|
||||||
func DisabledTestIBCTransfer(t *testing.T) {
|
func DisabledTestIBCTransfer(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
acc := getAccount(t, port, addr)
|
acc := getAccount(t, port, addr)
|
||||||
|
@ -332,7 +332,7 @@ func DisabledTestIBCTransfer(t *testing.T) {
|
||||||
func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
acc := getAccount(t, port, addr)
|
acc := getAccount(t, port, addr)
|
||||||
|
|
||||||
|
@ -372,8 +372,9 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
||||||
|
|
||||||
// broadcast tx
|
// broadcast tx
|
||||||
broadcastPayload := struct {
|
broadcastPayload := struct {
|
||||||
Tx auth.StdTx `json:"tx"`
|
Tx auth.StdTx `json:"tx"`
|
||||||
}{Tx: signedMsg}
|
Return string `json:"return"`
|
||||||
|
}{Tx: signedMsg, Return: "block"}
|
||||||
json, err = cdc.MarshalJSON(broadcastPayload)
|
json, err = cdc.MarshalJSON(broadcastPayload)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
res, body = Request(t, port, "POST", "/tx/broadcast", json)
|
res, body = Request(t, port, "POST", "/tx/broadcast", json)
|
||||||
|
@ -391,7 +392,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
||||||
func TestTxs(t *testing.T) {
|
func TestTxs(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// query wrong
|
// query wrong
|
||||||
|
@ -450,7 +451,7 @@ func TestTxs(t *testing.T) {
|
||||||
func TestPoolParamsQuery(t *testing.T) {
|
func TestPoolParamsQuery(t *testing.T) {
|
||||||
_, password := "test", "1234567890"
|
_, password := "test", "1234567890"
|
||||||
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
defaultParams := stake.DefaultParams()
|
defaultParams := stake.DefaultParams()
|
||||||
|
@ -484,9 +485,11 @@ func TestPoolParamsQuery(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatorsQuery(t *testing.T) {
|
func TestValidatorsQuery(t *testing.T) {
|
||||||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
require.Equal(t, 1, len(pks))
|
|
||||||
|
require.Equal(t, 1, len(valPubKeys))
|
||||||
|
require.Equal(t, 1, len(operAddrs))
|
||||||
|
|
||||||
validators := getValidators(t, port)
|
validators := getValidators(t, port)
|
||||||
require.Equal(t, len(validators), 1)
|
require.Equal(t, len(validators), 1)
|
||||||
|
@ -494,35 +497,35 @@ func TestValidatorsQuery(t *testing.T) {
|
||||||
// make sure all the validators were found (order unknown because sorted by operator addr)
|
// make sure all the validators were found (order unknown because sorted by operator addr)
|
||||||
foundVal := false
|
foundVal := false
|
||||||
|
|
||||||
if validators[0].ConsPubKey == pks[0] {
|
if validators[0].ConsPubKey == valPubKeys[0] {
|
||||||
foundVal = true
|
foundVal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, foundVal, "pk %v, operator %v", pks[0], validators[0].OperatorAddr)
|
require.True(t, foundVal, "pk %v, operator %v", operAddrs[0], validators[0].OperatorAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatorQuery(t *testing.T) {
|
func TestValidatorQuery(t *testing.T) {
|
||||||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
require.Equal(t, 1, len(pks))
|
require.Equal(t, 1, len(valPubKeys))
|
||||||
|
require.Equal(t, 1, len(operAddrs))
|
||||||
|
|
||||||
validator1Operator := sdk.ValAddress(pks[0].Address())
|
validator := getValidator(t, port, operAddrs[0])
|
||||||
validator := getValidator(t, port, validator1Operator)
|
assert.Equal(t, validator.OperatorAddr, operAddrs[0], "The returned validator does not hold the correct data")
|
||||||
assert.Equal(t, validator.OperatorAddr, validator1Operator, "The returned validator does not hold the correct data")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBonding(t *testing.T) {
|
func TestBonding(t *testing.T) {
|
||||||
name, password, denom := "test", "1234567890", "steak"
|
name, password, denom := "test", "1234567890", "steak"
|
||||||
addr, seed := CreateAddr(t, name, password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, name, password, GetKeyBase(t))
|
||||||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
|
||||||
|
cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
amt := sdk.NewDec(60)
|
amt := sdk.NewDec(60)
|
||||||
validator1Operator := sdk.ValAddress(pks[0].Address())
|
validator := getValidator(t, port, operAddrs[0])
|
||||||
validator := getValidator(t, port, validator1Operator)
|
|
||||||
|
|
||||||
// create bond TX
|
// create bond TX
|
||||||
resultTx := doDelegate(t, port, seed, name, password, addr, validator1Operator, 60)
|
resultTx := doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60)
|
||||||
tests.WaitForHeight(resultTx.Height+1, port)
|
tests.WaitForHeight(resultTx.Height+1, port)
|
||||||
|
|
||||||
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||||
|
@ -534,7 +537,7 @@ func TestBonding(t *testing.T) {
|
||||||
require.Equal(t, int64(40), coins.AmountOf(denom).Int64())
|
require.Equal(t, int64(40), coins.AmountOf(denom).Int64())
|
||||||
|
|
||||||
// query validator
|
// query validator
|
||||||
bond := getDelegation(t, port, addr, validator1Operator)
|
bond := getDelegation(t, port, addr, operAddrs[0])
|
||||||
require.Equal(t, amt, bond.Shares)
|
require.Equal(t, amt, bond.Shares)
|
||||||
|
|
||||||
summary := getDelegationSummary(t, port, addr)
|
summary := getDelegationSummary(t, port, addr)
|
||||||
|
@ -545,17 +548,17 @@ func TestBonding(t *testing.T) {
|
||||||
|
|
||||||
bondedValidators := getDelegatorValidators(t, port, addr)
|
bondedValidators := getDelegatorValidators(t, port, addr)
|
||||||
require.Len(t, bondedValidators, 1)
|
require.Len(t, bondedValidators, 1)
|
||||||
require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr)
|
require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr)
|
||||||
require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String())
|
require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String())
|
||||||
|
|
||||||
bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator)
|
bondedValidator := getDelegatorValidator(t, port, addr, operAddrs[0])
|
||||||
require.Equal(t, validator1Operator, bondedValidator.OperatorAddr)
|
require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr)
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// testing unbonding
|
// testing unbonding
|
||||||
|
|
||||||
// create unbond TX
|
// create unbond TX
|
||||||
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Operator, 60)
|
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, operAddrs[0], 60)
|
||||||
tests.WaitForHeight(resultTx.Height+1, port)
|
tests.WaitForHeight(resultTx.Height+1, port)
|
||||||
|
|
||||||
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||||
|
@ -566,7 +569,7 @@ func TestBonding(t *testing.T) {
|
||||||
coins = acc.GetCoins()
|
coins = acc.GetCoins()
|
||||||
require.Equal(t, int64(40), coins.AmountOf("steak").Int64())
|
require.Equal(t, int64(40), coins.AmountOf("steak").Int64())
|
||||||
|
|
||||||
unbonding := getUndelegation(t, port, addr, validator1Operator)
|
unbonding := getUndelegation(t, port, addr, operAddrs[0])
|
||||||
require.Equal(t, "60", unbonding.Balance.Amount.String())
|
require.Equal(t, "60", unbonding.Balance.Amount.String())
|
||||||
|
|
||||||
summary = getDelegationSummary(t, port, addr)
|
summary = getDelegationSummary(t, port, addr)
|
||||||
|
@ -599,7 +602,7 @@ func TestBonding(t *testing.T) {
|
||||||
func TestSubmitProposal(t *testing.T) {
|
func TestSubmitProposal(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create SubmitProposal TX
|
// create SubmitProposal TX
|
||||||
|
@ -621,7 +624,7 @@ func TestSubmitProposal(t *testing.T) {
|
||||||
func TestDeposit(t *testing.T) {
|
func TestDeposit(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create SubmitProposal TX
|
// create SubmitProposal TX
|
||||||
|
@ -655,7 +658,7 @@ func TestDeposit(t *testing.T) {
|
||||||
func TestVote(t *testing.T) {
|
func TestVote(t *testing.T) {
|
||||||
name, password := "test", "1234567890"
|
name, password := "test", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create SubmitProposal TX
|
// create SubmitProposal TX
|
||||||
|
@ -693,12 +696,12 @@ func TestVote(t *testing.T) {
|
||||||
func TestUnjail(t *testing.T) {
|
func TestUnjail(t *testing.T) {
|
||||||
_, password := "test", "1234567890"
|
_, password := "test", "1234567890"
|
||||||
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
|
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// XXX: any less than this and it fails
|
// XXX: any less than this and it fails
|
||||||
tests.WaitForHeight(3, port)
|
tests.WaitForHeight(3, port)
|
||||||
pkString, _ := sdk.Bech32ifyConsPub(pks[0])
|
pkString, _ := sdk.Bech32ifyConsPub(valPubKeys[0])
|
||||||
signingInfo := getSigningInfo(t, port, pkString)
|
signingInfo := getSigningInfo(t, port, pkString)
|
||||||
tests.WaitForHeight(4, port)
|
tests.WaitForHeight(4, port)
|
||||||
require.Equal(t, true, signingInfo.IndexOffset > 0)
|
require.Equal(t, true, signingInfo.IndexOffset > 0)
|
||||||
|
@ -711,7 +714,7 @@ func TestProposalsQuery(t *testing.T) {
|
||||||
name2, password2 := "test2", "1234567890"
|
name2, password2 := "test2", "1234567890"
|
||||||
addr, seed := CreateAddr(t, "test", password1, GetKeyBase(t))
|
addr, seed := CreateAddr(t, "test", password1, GetKeyBase(t))
|
||||||
addr2, seed2 := CreateAddr(t, "test2", password2, GetKeyBase(t))
|
addr2, seed2 := CreateAddr(t, "test2", password2, GetKeyBase(t))
|
||||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2})
|
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr, addr2})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// Addr1 proposes (and deposits) proposals #1 and #2
|
// Addr1 proposes (and deposits) proposals #1 and #2
|
||||||
|
@ -849,7 +852,7 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
||||||
}
|
}
|
||||||
}`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence))
|
}`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence))
|
||||||
|
|
||||||
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr)
|
res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers%v", receiveAddr, queryStr), jsonStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
|
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
|
||||||
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rakyll/statik/fs"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
@ -46,6 +47,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
|
||||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||||
listenAddr := viper.GetString(flagListenAddr)
|
listenAddr := viper.GetString(flagListenAddr)
|
||||||
handler := createHandler(cdc)
|
handler := createHandler(cdc)
|
||||||
|
registerSwaggerUI(handler)
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
|
||||||
maxOpen := viper.GetInt(flagMaxOpenConnections)
|
maxOpen := viper.GetInt(flagMaxOpenConnections)
|
||||||
sslHosts := viper.GetString(flagSSLHosts)
|
sslHosts := viper.GetString(flagSSLHosts)
|
||||||
|
@ -121,6 +123,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
|
||||||
cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node 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().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
|
||||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||||
|
cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response")
|
||||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||||
|
@ -128,7 +131,7 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandler(cdc *codec.Codec) http.Handler {
|
func createHandler(cdc *codec.Codec) *mux.Router {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
kb, err := keys.GetKeyBase() //XXX
|
kb, err := keys.GetKeyBase() //XXX
|
||||||
|
@ -154,6 +157,15 @@ func createHandler(cdc *codec.Codec) http.Handler {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func registerSwaggerUI(r *mux.Router) {
|
||||||
|
statikFS, err := fs.New()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
staticServer := http.FileServer(statikFS)
|
||||||
|
r.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer))
|
||||||
|
}
|
||||||
|
|
||||||
func validateCertKeyFiles(certFile, keyFile string) error {
|
func validateCertKeyFiles(certFile, keyFile string) error {
|
||||||
if keyFile == "" {
|
if keyFile == "" {
|
||||||
return errors.New("a key file is required")
|
return errors.New("a key file is required")
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package statik
|
||||||
|
|
||||||
|
//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/lcd/statik
|
Binary file not shown.
After Width: | Height: | Size: 445 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,60 @@
|
||||||
|
<!-- HTML for static distribution bundle build -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Swagger UI</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||||
|
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||||
|
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||||
|
<style>
|
||||||
|
html
|
||||||
|
{
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after
|
||||||
|
{
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
margin:0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
|
<script src="swagger-ui-bundle.js"> </script>
|
||||||
|
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
|
||||||
|
// Build a system
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "./swagger.yaml",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
})
|
||||||
|
|
||||||
|
window.ui = ui
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en-US">
|
||||||
|
<body onload="run()">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
function run () {
|
||||||
|
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||||
|
var sentState = oauth2.state;
|
||||||
|
var redirectUrl = oauth2.redirectUrl;
|
||||||
|
var isValid, qp, arr;
|
||||||
|
|
||||||
|
if (/code|token|error/.test(window.location.hash)) {
|
||||||
|
qp = window.location.hash.substring(1);
|
||||||
|
} else {
|
||||||
|
qp = location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = qp.split("&")
|
||||||
|
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||||
|
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||||
|
function (key, value) {
|
||||||
|
return key === "" ? value : decodeURIComponent(value)
|
||||||
|
}
|
||||||
|
) : {}
|
||||||
|
|
||||||
|
isValid = qp.state === sentState
|
||||||
|
|
||||||
|
if ((
|
||||||
|
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||||
|
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||||
|
) && !oauth2.auth.code) {
|
||||||
|
if (!isValid) {
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "warning",
|
||||||
|
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qp.code) {
|
||||||
|
delete oauth2.state;
|
||||||
|
oauth2.auth.code = qp.code;
|
||||||
|
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||||
|
} else {
|
||||||
|
let oauthErrorMsg
|
||||||
|
if (qp.error) {
|
||||||
|
oauthErrorMsg = "["+qp.error+"]: " +
|
||||||
|
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||||
|
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "error",
|
||||||
|
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
</script>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,704 @@
|
||||||
|
---
|
||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
version: 1.1.0
|
||||||
|
title: Gaia-Lite (former LCD) to interface with Cosmos BaseServer via REST
|
||||||
|
description: Specification for Gaia-lite provided by `gaiacli rest-server`
|
||||||
|
tags:
|
||||||
|
- name: ICS0
|
||||||
|
description: Tendermint APIs, such as query blocks, transactions and validatorset
|
||||||
|
- name: ICS20
|
||||||
|
description: Create and sign a send tx
|
||||||
|
- name: version
|
||||||
|
description: Information about the app version
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
securityDefinitions:
|
||||||
|
kms:
|
||||||
|
type: basic
|
||||||
|
paths:
|
||||||
|
/version:
|
||||||
|
get:
|
||||||
|
summary: Version of Gaia-lite
|
||||||
|
tags:
|
||||||
|
- version
|
||||||
|
description: Get the version of gaia-lite running locally to compare against expected
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Plaintext version i.e. "v0.5.0"
|
||||||
|
/node_version:
|
||||||
|
get:
|
||||||
|
summary: Version of the connected node
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
description: Get the version of the SDK running on the connected node to compare against expected
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Plaintext version i.e. "v0.5.0"
|
||||||
|
/node_info:
|
||||||
|
get:
|
||||||
|
description: Information about the connected node
|
||||||
|
summary: The properties of the connected node
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Node status
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
moniker:
|
||||||
|
type: string
|
||||||
|
example: validator-name
|
||||||
|
network:
|
||||||
|
type: string
|
||||||
|
example: gaia-2
|
||||||
|
channels:
|
||||||
|
type: string
|
||||||
|
listen_addr:
|
||||||
|
type: string
|
||||||
|
example: 192.168.56.1:26656
|
||||||
|
version:
|
||||||
|
description: Tendermint version
|
||||||
|
type: string
|
||||||
|
example: 0.15.0
|
||||||
|
other:
|
||||||
|
description: more information on versions
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
/syncing:
|
||||||
|
get:
|
||||||
|
summary: Syncing state of node
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
description: Get if the node is currently syning with other nodes
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: '"true" or "false"'
|
||||||
|
/blocks/latest:
|
||||||
|
get:
|
||||||
|
summary: Get the latest block
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The latest block
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/QueryBlock"
|
||||||
|
/blocks/{height}:
|
||||||
|
get:
|
||||||
|
summary: Get a block at a certain height
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: height
|
||||||
|
description: Block height
|
||||||
|
required: true
|
||||||
|
type: number
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The block at a specific height
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/QueryBlock"
|
||||||
|
404:
|
||||||
|
description: Block at height is not available
|
||||||
|
/validatorsets/latest:
|
||||||
|
get:
|
||||||
|
summary: Get the latest validator set
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The validator set at the latest block height
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
block_height:
|
||||||
|
type: number
|
||||||
|
validators:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Validator"
|
||||||
|
/validatorsets/{height}:
|
||||||
|
get:
|
||||||
|
summary: Get a validator set a certain height
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: height
|
||||||
|
description: Block height
|
||||||
|
required: true
|
||||||
|
type: number
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The validator set at a specific block height
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
block_height:
|
||||||
|
type: number
|
||||||
|
validators:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Validator"
|
||||||
|
404:
|
||||||
|
description: Block at height not available
|
||||||
|
/txs/{hash}:
|
||||||
|
get:
|
||||||
|
summary: Get a Tx by hash
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: hash
|
||||||
|
description: Tx hash
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Tx with the provided hash
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TxQuery"
|
||||||
|
404:
|
||||||
|
description: Tx not available for provided hash
|
||||||
|
/txs:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
summary: Search transactions
|
||||||
|
description: Search transactions by tag
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: tag
|
||||||
|
type: string
|
||||||
|
description: "transaction tag, for instance: sender_bech32=`'cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'`"
|
||||||
|
required: true
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
description: Pagination page
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: size
|
||||||
|
description: Pagination size
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: All Tx matching the provided tags
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/TxQuery"
|
||||||
|
404:
|
||||||
|
description: Pagination is out of bounds
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- ICS0
|
||||||
|
summary: broadcast Tx
|
||||||
|
description: broadcast tx with tendermint rpc
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: txBroadcast
|
||||||
|
description: Build a StdTx transaction and serilize it to a byte array with amino, then the `"tx"` field in the post body will be the base64 encoding of the byte array. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away).
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TendertmintTx"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Broadcast tx result
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/BroadcastTxCommitResult"
|
||||||
|
/tx/sign:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- ICS20
|
||||||
|
summary: Sign a Tx
|
||||||
|
description: Sign a Tx providing locally stored account and according password
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: sendToken
|
||||||
|
description: sign tx
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TxSign"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The signed Tx
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/StdTx"
|
||||||
|
401:
|
||||||
|
description: Account name and/or password where wrong
|
||||||
|
/tx/broadcast:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- ICS20
|
||||||
|
summary: Send a signed Tx
|
||||||
|
description: Send a signed Tx to a Gaiad full node
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: txBroadcast
|
||||||
|
description: broadcast tx
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TxBroadcast"
|
||||||
|
responses:
|
||||||
|
202:
|
||||||
|
description: Tx was send and will probably be added to the next block
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/BroadcastTxCommitResult"
|
||||||
|
400:
|
||||||
|
description: The Tx was malformated
|
||||||
|
/bank/balances/{address}:
|
||||||
|
get:
|
||||||
|
summary: Get the account balances
|
||||||
|
tags:
|
||||||
|
- ICS20
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: address
|
||||||
|
description: Account address in bech32 format
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Account balances
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Coin"
|
||||||
|
204:
|
||||||
|
description: There is no data for the requested account. This is not a 404 as the account might exist, just does not hold data.
|
||||||
|
/bank/accounts/{address}/transfers:
|
||||||
|
post:
|
||||||
|
summary: Send coins (build -> sign -> send)
|
||||||
|
description: Send coins (build -> sign -> send)
|
||||||
|
tags:
|
||||||
|
- ICS20
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: simulate
|
||||||
|
description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
- in: query
|
||||||
|
name: generate_only
|
||||||
|
description: if true, build an unsigned transaction and write it back
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
- in: path
|
||||||
|
name: address
|
||||||
|
description: Account address in bech32 format
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: body
|
||||||
|
name: account
|
||||||
|
description: The password of the account to remove from the KMS
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
base_req:
|
||||||
|
$ref: "#/definitions/BaseReq"
|
||||||
|
amount:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Coin"
|
||||||
|
responses:
|
||||||
|
202:
|
||||||
|
description: Tx was send and will probably be added to the next block
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/BroadcastTxCommitResult"
|
||||||
|
400:
|
||||||
|
description: The Tx was malformated
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
CheckTxResult:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: integer
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
gas_used:
|
||||||
|
type: integer
|
||||||
|
gas_wanted:
|
||||||
|
type: integer
|
||||||
|
info:
|
||||||
|
type: string
|
||||||
|
log:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
"$ref": "#/definitions/KVPair"
|
||||||
|
example:
|
||||||
|
code: 0
|
||||||
|
data: data
|
||||||
|
log: log
|
||||||
|
gas_used: 5000
|
||||||
|
gas_wanted: 10000
|
||||||
|
info: info
|
||||||
|
tags:
|
||||||
|
- ''
|
||||||
|
- ''
|
||||||
|
DeliverTxResult:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: integer
|
||||||
|
data:
|
||||||
|
type: string
|
||||||
|
gas_used:
|
||||||
|
type: integer
|
||||||
|
gas_wanted:
|
||||||
|
type: integer
|
||||||
|
info:
|
||||||
|
type: string
|
||||||
|
log:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
"$ref": "#/definitions/KVPair"
|
||||||
|
example:
|
||||||
|
code: 5
|
||||||
|
data: data
|
||||||
|
log: log
|
||||||
|
gas_used: 5000
|
||||||
|
gas_wanted: 10000
|
||||||
|
info: info
|
||||||
|
tags:
|
||||||
|
- ''
|
||||||
|
- ''
|
||||||
|
BroadcastTxCommitResult:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
check_tx:
|
||||||
|
$ref: "#/definitions/CheckTxResult"
|
||||||
|
deliver_tx:
|
||||||
|
$ref: "#/definitions/DeliverTxResult"
|
||||||
|
hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
height:
|
||||||
|
type: integer
|
||||||
|
KVPair:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
Fee:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
gas:
|
||||||
|
type: string
|
||||||
|
amount:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Coin"
|
||||||
|
Msg:
|
||||||
|
type: string
|
||||||
|
Address:
|
||||||
|
type: string
|
||||||
|
description: bech32 encoded addres
|
||||||
|
example: cosmoszgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||||
|
ValidatorAddress:
|
||||||
|
type: string
|
||||||
|
description: bech32 encoded addres
|
||||||
|
example: cosmosvaloper:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||||
|
PubKey:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
example: "tendermint/PubKeySecp256k1"
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH"
|
||||||
|
ValidatorPubKey:
|
||||||
|
type: string
|
||||||
|
description: bech32 encoded public key
|
||||||
|
example: cosmosvalconspub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||||
|
Coin:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
denom:
|
||||||
|
type: string
|
||||||
|
example: steak
|
||||||
|
amount:
|
||||||
|
type: string
|
||||||
|
example: "50"
|
||||||
|
Hash:
|
||||||
|
type: string
|
||||||
|
example: EE5F3404034C524501629B56E0DDC38FAD651F04
|
||||||
|
Result:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
log:
|
||||||
|
type: string
|
||||||
|
gas_wanted:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
gas_used:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/KVPair"
|
||||||
|
TxQuery:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
hash:
|
||||||
|
type: string
|
||||||
|
height:
|
||||||
|
type: number
|
||||||
|
tx:
|
||||||
|
$ref: "#/definitions/StdTx"
|
||||||
|
result:
|
||||||
|
$ref: "#/definitions/Result"
|
||||||
|
TendertmintTx:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tx:
|
||||||
|
type: string
|
||||||
|
return:
|
||||||
|
type: string
|
||||||
|
example: block
|
||||||
|
TxBroadcast:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tx:
|
||||||
|
$ref: "#/definitions/StdTx"
|
||||||
|
TxSign:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tx:
|
||||||
|
$ref: "#/definitions/StdTx"
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
chain_id:
|
||||||
|
type: string
|
||||||
|
account_number:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
append_sig:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
StdTx:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
msg:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Msg"
|
||||||
|
fee:
|
||||||
|
$ref: "#/definitions/Fee"
|
||||||
|
memo:
|
||||||
|
type: string
|
||||||
|
signature:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
signature:
|
||||||
|
type: string
|
||||||
|
example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY=
|
||||||
|
pub_key:
|
||||||
|
$ref: "#/definitions/PubKey"
|
||||||
|
account_number:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
Account:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: Main Account
|
||||||
|
address:
|
||||||
|
$ref: "#/definitions/Address"
|
||||||
|
pub_key:
|
||||||
|
$ref: "#/definitions/PubKey"
|
||||||
|
BlockID:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
parts:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
total:
|
||||||
|
type: number
|
||||||
|
example: 0
|
||||||
|
hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
BlockHeader:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
chain_id:
|
||||||
|
type: string
|
||||||
|
example: gaia-2
|
||||||
|
height:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
time:
|
||||||
|
type: string
|
||||||
|
example: '2017-12-30T05:53:09.287+01:00'
|
||||||
|
num_txs:
|
||||||
|
type: number
|
||||||
|
example: 0
|
||||||
|
last_block_id:
|
||||||
|
$ref: "#/definitions/BlockID"
|
||||||
|
total_txs:
|
||||||
|
type: number
|
||||||
|
example: 35
|
||||||
|
last_commit_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
data_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
validators_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
consensus_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
app_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
last_results_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
evidence_hash:
|
||||||
|
$ref: "#/definitions/Hash"
|
||||||
|
Block:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
header:
|
||||||
|
$ref: "#/definitions/BlockHeader"
|
||||||
|
txs:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
evidence:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
last_commit:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
block_id:
|
||||||
|
$ref: "#/definitions/BlockID"
|
||||||
|
precommits:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
validator_address:
|
||||||
|
type: string
|
||||||
|
validator_index:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
height:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
round:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
timestamp:
|
||||||
|
type: string
|
||||||
|
example: '2017-12-30T05:53:09.287+01:00'
|
||||||
|
type:
|
||||||
|
type: number
|
||||||
|
example: 2
|
||||||
|
block_id:
|
||||||
|
$ref: "#/definitions/BlockID"
|
||||||
|
signature:
|
||||||
|
type: string
|
||||||
|
example: '7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ=='
|
||||||
|
BlockMeta:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
header:
|
||||||
|
$ref: "#/definitions/BlockHeader"
|
||||||
|
block_id:
|
||||||
|
$ref: "#/definitions/BlockID"
|
||||||
|
QueryBlock:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
block_meta:
|
||||||
|
$ref: "#/definitions/BlockMeta"
|
||||||
|
block:
|
||||||
|
$ref: "#/definitions/Block"
|
||||||
|
BaseReq:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
chain_id:
|
||||||
|
type: string
|
||||||
|
account_number:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
example: "0"
|
||||||
|
gas:
|
||||||
|
type: string
|
||||||
|
example: "200000"
|
||||||
|
gas_adjustment:
|
||||||
|
type: string
|
||||||
|
example: "1.2"
|
||||||
|
Validator:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
address:
|
||||||
|
$ref: '#/definitions/ValidatorAddress'
|
||||||
|
pub_key:
|
||||||
|
$ref: "#/definitions/ValidatorPubKey"
|
||||||
|
power:
|
||||||
|
type: number
|
||||||
|
example: 1000
|
||||||
|
accum:
|
||||||
|
type: number
|
||||||
|
example: 1000
|
|
@ -115,7 +115,14 @@ func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.Acc
|
||||||
// their respective sockets where nValidators is the total number of validators
|
// their respective sockets where nValidators is the total number of validators
|
||||||
// and initAddrs are the accounts to initialize with some steak tokens. It
|
// and initAddrs are the accounts to initialize with some steak tokens. It
|
||||||
// returns a cleanup function, a set of validator public keys, and a port.
|
// returns a cleanup function, a set of validator public keys, and a port.
|
||||||
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) {
|
func InitializeTestLCD(
|
||||||
|
t *testing.T, nValidators int, initAddrs []sdk.AccAddress,
|
||||||
|
) (cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) {
|
||||||
|
|
||||||
|
if nValidators < 1 {
|
||||||
|
panic("InitializeTestLCD must use at least one validator")
|
||||||
|
}
|
||||||
|
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
config.Consensus.TimeoutCommit = 100
|
config.Consensus.TimeoutCommit = 100
|
||||||
config.Consensus.SkipTimeoutCommit = false
|
config.Consensus.SkipTimeoutCommit = false
|
||||||
|
@ -136,30 +143,34 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
||||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if nValidators < 1 {
|
// append initial (proposing) validator
|
||||||
panic("InitializeTestLCD must use at least one validator")
|
genDoc.Validators[0] = tmtypes.GenesisValidator{
|
||||||
|
PubKey: privVal.GetPubKey(),
|
||||||
|
Power: 999999, // create enough power to enable 2/3 voting power
|
||||||
|
Name: "validator-1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append any additional (non-proposing) validators
|
||||||
for i := 1; i < nValidators; i++ {
|
for i := 1; i < nValidators; i++ {
|
||||||
genDoc.Validators = append(genDoc.Validators,
|
genDoc.Validators = append(genDoc.Validators,
|
||||||
tmtypes.GenesisValidator{
|
tmtypes.GenesisValidator{
|
||||||
PubKey: ed25519.GenPrivKey().PubKey(),
|
PubKey: ed25519.GenPrivKey().PubKey(),
|
||||||
Power: 1,
|
Power: 1,
|
||||||
Name: "val",
|
Name: fmt.Sprintf("validator-%d", i+1),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var validatorsPKs []crypto.PubKey
|
|
||||||
|
|
||||||
// NOTE: It's bad practice to reuse public key address for the operator
|
|
||||||
// address but doing in the test for simplicity.
|
|
||||||
var appGenTxs []json.RawMessage
|
var appGenTxs []json.RawMessage
|
||||||
for _, gdValidator := range genDoc.Validators {
|
|
||||||
pk := gdValidator.PubKey
|
|
||||||
validatorsPKs = append(validatorsPKs, pk)
|
|
||||||
|
|
||||||
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1")
|
for _, gdValidator := range genDoc.Validators {
|
||||||
|
operAddr := ed25519.GenPrivKey().PubKey().Address()
|
||||||
|
pk := gdValidator.PubKey
|
||||||
|
|
||||||
|
valConsPubKeys = append(valConsPubKeys, pk)
|
||||||
|
valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr))
|
||||||
|
|
||||||
|
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(operAddr), gdValidator.Name)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
appGenTxs = append(appGenTxs, appGenTx)
|
appGenTxs = append(appGenTxs, appGenTx)
|
||||||
|
@ -203,14 +214,14 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
||||||
tests.WaitForLCDStart(port)
|
tests.WaitForLCDStart(port)
|
||||||
tests.WaitForHeight(1, port)
|
tests.WaitForHeight(1, port)
|
||||||
|
|
||||||
cleanup := func() {
|
cleanup = func() {
|
||||||
logger.Debug("cleaning up LCD initialization")
|
logger.Debug("cleaning up LCD initialization")
|
||||||
node.Stop()
|
node.Stop()
|
||||||
node.Wait()
|
node.Wait()
|
||||||
lcd.Close()
|
lcd.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanup, validatorsPKs, port
|
return cleanup, valConsPubKeys, valOperAddrs, port
|
||||||
}
|
}
|
||||||
|
|
||||||
// startTM creates and starts an in-process Tendermint node with memDB and
|
// startTM creates and starts an in-process Tendermint node with memDB and
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -62,13 +63,10 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move maarshalling into cmd/rest functions
|
if cliCtx.Indent {
|
||||||
// output, err := tmcodec.MarshalJSON(res)
|
return cdc.MarshalJSONIndent(res, "", " ")
|
||||||
output, err := cdc.MarshalJSON(res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return output, nil
|
return cdc.MarshalJSON(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current blockchain height
|
// get the current blockchain height
|
||||||
|
@ -133,7 +131,7 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Write(output)
|
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +150,6 @@ func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Write(output)
|
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
)
|
)
|
||||||
|
@ -22,6 +23,7 @@ func statusCommand() *cobra.Command {
|
||||||
|
|
||||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||||
|
cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,13 +40,20 @@ func getNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) {
|
||||||
// CMD
|
// CMD
|
||||||
|
|
||||||
func printNodeStatus(cmd *cobra.Command, args []string) error {
|
func printNodeStatus(cmd *cobra.Command, args []string) error {
|
||||||
status, err := getNodeStatus(context.NewCLIContext())
|
// No need to verify proof in getting node status
|
||||||
|
viper.Set(client.FlagTrustNode, true)
|
||||||
|
cliCtx := context.NewCLIContext()
|
||||||
|
status, err := getNodeStatus(cliCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := cdc.MarshalJSON(status)
|
var output []byte
|
||||||
// output, err := cdc.MarshalJSONIndent(res, " ", "")
|
if cliCtx.Indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(status, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(status)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -66,14 +75,7 @@ func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeInfo := status.NodeInfo
|
nodeInfo := status.NodeInfo
|
||||||
output, err := cdc.MarshalJSON(nodeInfo)
|
utils.PostProcessResponse(w, cdc, nodeInfo, cliCtx.Indent)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO these next two functions feel kinda hacky based on their placement
|
// TODO these next two functions feel kinda hacky based on their placement
|
||||||
|
@ -98,12 +99,11 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := cdc.MarshalJSON(outputValidatorsRes)
|
if cliCtx.Indent {
|
||||||
if err != nil {
|
return cdc.MarshalJSONIndent(outputValidatorsRes, "", " ")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return cdc.MarshalJSON(outputValidatorsRes)
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CMD
|
// CMD
|
||||||
|
@ -158,8 +158,7 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +178,6 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,60 @@
|
||||||
package tx
|
package tx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tx Broadcast Body
|
const (
|
||||||
type BroadcastTxBody struct {
|
// Returns with the response from CheckTx.
|
||||||
TxBytes string `json:"tx"`
|
flagSync = "sync"
|
||||||
|
// Returns right away, with no response
|
||||||
|
flagAsync = "async"
|
||||||
|
// Only returns error if mempool.BroadcastTx errs (ie. problem with the app) or if we timeout waiting for tx to commit.
|
||||||
|
flagBlock = "block"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BroadcastBody Tx Broadcast Body
|
||||||
|
type BroadcastBody struct {
|
||||||
|
TxBytes []byte `json:"tx"`
|
||||||
|
Return string `json:"return"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastTx REST Handler
|
// BroadcastTxRequest REST Handler
|
||||||
func BroadcastTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
// nolint: gocyclo
|
||||||
|
func BroadcastTxRequest(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var m BroadcastTxBody
|
var m BroadcastBody
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
err := decoder.Decode(&m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = cdc.UnmarshalJSON(body, &m)
|
||||||
res, err := cliCtx.BroadcastTxAndAwaitCommit([]byte(m.TxBytes))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var res interface{}
|
||||||
w.Write([]byte(string(res.Height)))
|
switch m.Return {
|
||||||
|
case flagBlock:
|
||||||
|
res, err = cliCtx.BroadcastTx(m.TxBytes)
|
||||||
|
case flagSync:
|
||||||
|
res, err = cliCtx.BroadcastTxSync(m.TxBytes)
|
||||||
|
case flagAsync:
|
||||||
|
res, err = cliCtx.BroadcastTxAsync(m.TxBytes)
|
||||||
|
default:
|
||||||
|
utils.WriteErrorResponse(w, http.StatusInternalServerError, "unsupported return type. supported types: block, sync, async")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.PostProcessResponse(w, cdc, res, cliCtx.Indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
@ -78,7 +79,10 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([]
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return codec.MarshalJSONIndent(cdc, info)
|
if cliCtx.Indent {
|
||||||
|
return cdc.MarshalJSONIndent(info, "", " ")
|
||||||
|
}
|
||||||
|
return cdc.MarshalJSON(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateTxResult performs transaction verification
|
// ValidateTxResult performs transaction verification
|
||||||
|
@ -142,7 +146,6 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.PostProcessResponse(w, cdc, output, cliCtx.Indent)
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,5 @@ func AddCommands(cmd *cobra.Command, cdc *codec.Codec) {
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
||||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
|
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
|
||||||
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
|
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
|
||||||
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
|
r.HandleFunc("/txs", BroadcastTxRequest(cliCtx, cdc)).Methods("POST")
|
||||||
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,7 +52,13 @@ $ gaiacli tendermint txs --tag test1,test2 --any
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := cdc.MarshalJSON(txs)
|
var output []byte
|
||||||
|
if cliCtx.Indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(txs, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(txs)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -174,13 +181,6 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := cdc.MarshalJSON(txs)
|
utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -8,11 +9,11 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
client "github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -240,11 +241,27 @@ func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx c
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := codec.MarshalJSONIndent(cdc, res)
|
PostProcessResponse(w, cdc, res, cliCtx.Indent)
|
||||||
if err != nil {
|
}
|
||||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// PostProcessResponse performs post process for rest response
|
||||||
|
func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response interface{}, indent bool) {
|
||||||
|
var output []byte
|
||||||
|
switch response.(type) {
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
if indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(response, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(response)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case []byte:
|
||||||
|
output = response.([]byte)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Write(output)
|
w.Write(output)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
|
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
|
||||||
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
|
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
|
||||||
|
|
||||||
|
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
|
||||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"os"
|
"os"
|
||||||
|
|
|
@ -15,7 +15,8 @@ module.exports = {
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
children: [
|
children: [
|
||||||
"/introduction/cosmos-hub",
|
"/introduction/cosmos-hub",
|
||||||
"/introduction/tendermint",
|
"/introduction/tendermint-cosmos",
|
||||||
|
"/introduction/tendermint"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -54,6 +55,18 @@ module.exports = {
|
||||||
// "/specs/icts",
|
// "/specs/icts",
|
||||||
// ]
|
// ]
|
||||||
// },
|
// },
|
||||||
|
{
|
||||||
|
title: "SDK by Examples - Simple Governance",
|
||||||
|
collapsable: false,
|
||||||
|
children: [
|
||||||
|
["/sdk/sdk-by-examples/simple-governance/intro", "Intro"],
|
||||||
|
"/sdk/sdk-by-examples/simple-governance/setup-and-design",
|
||||||
|
"/sdk/sdk-by-examples/simple-governance/app-init",
|
||||||
|
"/sdk/sdk-by-examples/simple-governance/simple-gov-module",
|
||||||
|
"/sdk/sdk-by-examples/simple-governance/bridging-it-all",
|
||||||
|
"/sdk/sdk-by-examples/simple-governance/running-the-application"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Lotion JS",
|
title: "Lotion JS",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
|
@ -71,6 +84,13 @@ module.exports = {
|
||||||
"/validators/validator-faq"
|
"/validators/validator-faq"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Clients",
|
||||||
|
collapsable: false,
|
||||||
|
children: [
|
||||||
|
["/clients/service-providers", "Service Providers"]
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Resources",
|
title: "Resources",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
|
|
|
@ -10,6 +10,11 @@ built from the files in this (`/docs`) directory for
|
||||||
and [develop](https://github.com/cosmos/cosmos-sdk/tree/develop/docs),
|
and [develop](https://github.com/cosmos/cosmos-sdk/tree/develop/docs),
|
||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
|
Besides, gaia-lite API docs are also provided by gaia-lite. The default API docs page is:
|
||||||
|
```
|
||||||
|
https://localhost:1317/swagger-ui/
|
||||||
|
```
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
There is a Jenkins job listening for changes in the `/docs` directory, on both
|
There is a Jenkins job listening for changes in the `/docs` directory, on both
|
||||||
|
@ -93,3 +98,18 @@ then navigate to localhost:8080 in your browser.
|
||||||
|
|
||||||
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
||||||
much as possible with its [counterpart in the Tendermint Core repo](https://github.com/tendermint/tendermint/blob/develop/docs/DOCS_README.md).
|
much as possible with its [counterpart in the Tendermint Core repo](https://github.com/tendermint/tendermint/blob/develop/docs/DOCS_README.md).
|
||||||
|
|
||||||
|
## Update and Build the RPC docs
|
||||||
|
|
||||||
|
1. Execute the following command at the root directory to install the swagger-ui generate tool.
|
||||||
|
```
|
||||||
|
make get_tools
|
||||||
|
```
|
||||||
|
2. Edit API docs
|
||||||
|
1. Directly Edit API docs manually: `client/lcd/swagger-ui/swagger.yaml`.
|
||||||
|
2. Edit API docs within [SwaggerHub](https://app.swaggerhub.com). Please refer to this [document](https://app.swaggerhub.com/help/index) for how to use the about website to edit API docs.
|
||||||
|
3. Download `swagger.yaml` and replace the old `swagger.yaml` under fold `client/lcd/swagger-ui`.
|
||||||
|
4. Compile gaiacli
|
||||||
|
```
|
||||||
|
make install
|
||||||
|
```
|
|
@ -10,9 +10,9 @@ Cosmos can interoperate with multiple other applications and cryptocurrencies. B
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
- [Getting started with the SDK](./sdk/core/intro.html)
|
- [Getting started with the SDK](./sdk/core/intro.md)
|
||||||
- [SDK Examples](../examples)
|
- [SDK Examples](../examples)
|
||||||
- [Join the testnet](./getting-started/full-node.html#run-a-full-node)
|
- [Join the testnet](./getting-started/join-testnet.md#run-a-full-node)
|
||||||
|
|
||||||
## Edit the Documentation
|
## Edit the Documentation
|
||||||
|
|
||||||
|
|
|
@ -1,936 +0,0 @@
|
||||||
swagger: '2.0'
|
|
||||||
info:
|
|
||||||
version: '1.1.0'
|
|
||||||
title: Gaia-Lite (former LCD) to interface with Cosmos BaseServer via REST
|
|
||||||
description: Specification for Gaia-lite provided by `gaiacli rest-server`
|
|
||||||
|
|
||||||
tags:
|
|
||||||
- name: keys
|
|
||||||
description: Key management to add or view local private keys
|
|
||||||
- name: send
|
|
||||||
description: Create and sign a send tx
|
|
||||||
- name: stake
|
|
||||||
description: Stake module API for staking and validation
|
|
||||||
- name: account
|
|
||||||
description: Query account balance
|
|
||||||
- name: query
|
|
||||||
description: Information about blocks and txs
|
|
||||||
- name: validator set
|
|
||||||
description: Check the state of the validator set
|
|
||||||
- name: node
|
|
||||||
description: Information of the connected node
|
|
||||||
- name: version
|
|
||||||
description: Information about the app version
|
|
||||||
|
|
||||||
|
|
||||||
securityDefinitions:
|
|
||||||
kms:
|
|
||||||
type: basic
|
|
||||||
|
|
||||||
paths:
|
|
||||||
/version:
|
|
||||||
get:
|
|
||||||
summary: Version of Gaia-lite
|
|
||||||
tags:
|
|
||||||
- version
|
|
||||||
description: Get the version of gaia-lite running locally to compare against expected
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Plaintext version i.e. "v0.5.0"
|
|
||||||
/node_version:
|
|
||||||
get:
|
|
||||||
summary: Version of the connected node
|
|
||||||
tags:
|
|
||||||
- node
|
|
||||||
description: Get the version of the SDK running on the connected node to compare against expected
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Plaintext version i.e. "v0.5.0"
|
|
||||||
/node_info:
|
|
||||||
get:
|
|
||||||
description: Information about the connected node
|
|
||||||
summary: The properties of the connected node
|
|
||||||
tags:
|
|
||||||
- node
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Node status
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
pub_key:
|
|
||||||
$ref: '#/definitions/PubKey'
|
|
||||||
moniker:
|
|
||||||
type: string
|
|
||||||
example: 159.89.198.221
|
|
||||||
network:
|
|
||||||
type: string
|
|
||||||
example: gaia-2
|
|
||||||
remote_addr:
|
|
||||||
type: string
|
|
||||||
listen_addr:
|
|
||||||
type: string
|
|
||||||
example: 192.168.56.1:26656
|
|
||||||
version:
|
|
||||||
description: Tendermint version
|
|
||||||
type: string
|
|
||||||
example: 0.15.0
|
|
||||||
other:
|
|
||||||
description: more information on versions
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
/syncing:
|
|
||||||
get:
|
|
||||||
summary: Syncing state of node
|
|
||||||
tags:
|
|
||||||
- node
|
|
||||||
description: Get if the node is currently syning with other nodes
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: '"true" or "false"'
|
|
||||||
|
|
||||||
/keys:
|
|
||||||
get:
|
|
||||||
summary: List of accounts stored locally
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Array of accounts
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/Account'
|
|
||||||
post:
|
|
||||||
summary: Create a new account locally
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: account
|
|
||||||
description: The account to create
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
- password
|
|
||||||
- seed
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
seed:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Returns address of the account created
|
|
||||||
/keys/seed:
|
|
||||||
get:
|
|
||||||
summary: Create a new seed to create a new account with
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: 16 word Seed
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
/keys/{name}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: name
|
|
||||||
description: Account name
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Get a certain locally stored account
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Locally stored account
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Account"
|
|
||||||
404:
|
|
||||||
description: Account is not available
|
|
||||||
put:
|
|
||||||
summary: Update the password for this account in the KMS
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: account
|
|
||||||
description: The new and old password
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- new_password
|
|
||||||
- old_password
|
|
||||||
properties:
|
|
||||||
new_password:
|
|
||||||
type: string
|
|
||||||
old_password:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Updated password
|
|
||||||
401:
|
|
||||||
description: Password is wrong
|
|
||||||
404:
|
|
||||||
description: Account is not available
|
|
||||||
delete:
|
|
||||||
summary: Remove an account
|
|
||||||
tags:
|
|
||||||
- keys
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: account
|
|
||||||
description: The password of the account to remove from the KMS
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- password
|
|
||||||
properties:
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Removed account
|
|
||||||
401:
|
|
||||||
description: Password is wrong
|
|
||||||
404:
|
|
||||||
description: Account is not available
|
|
||||||
# /accounts/send:
|
|
||||||
# post:
|
|
||||||
# summary: Send coins (build -> sign -> send)
|
|
||||||
# security:
|
|
||||||
# - sign: []
|
|
||||||
# requestBody:
|
|
||||||
# content:
|
|
||||||
# application/json:
|
|
||||||
# schema:
|
|
||||||
# type: object
|
|
||||||
# properties:
|
|
||||||
# fees:
|
|
||||||
# $ref: "#/definitions/Coins"
|
|
||||||
# outputs:
|
|
||||||
# type: array
|
|
||||||
# items:
|
|
||||||
# type: object
|
|
||||||
# properties:
|
|
||||||
# pub_key:
|
|
||||||
# $ref: "#/definitions/PubKey"
|
|
||||||
# amount:
|
|
||||||
# type: array
|
|
||||||
# items:
|
|
||||||
# $ref: "#/definitions/Coins"
|
|
||||||
# responses:
|
|
||||||
# 202:
|
|
||||||
# description: Tx was send and will probably be added to the next block
|
|
||||||
# 400:
|
|
||||||
# description: The Tx was malformated
|
|
||||||
|
|
||||||
/accounts/{address}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: address
|
|
||||||
description: Account address in bech32 format
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Get the account balances
|
|
||||||
tags:
|
|
||||||
- account
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Account balances
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Balance"
|
|
||||||
204:
|
|
||||||
description: There is no data for the requested account. This is not a 404 as the account might exist, just does not hold data.
|
|
||||||
/accounts/{address}/send:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: address
|
|
||||||
description: Account address in bech32 format
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
post:
|
|
||||||
summary: Send coins (build -> sign -> send)
|
|
||||||
tags:
|
|
||||||
- send
|
|
||||||
security:
|
|
||||||
- kms: []
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: account
|
|
||||||
description: The password of the account to remove from the KMS
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
amount:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Coins"
|
|
||||||
chain_id:
|
|
||||||
type: string
|
|
||||||
squence:
|
|
||||||
type: number
|
|
||||||
responses:
|
|
||||||
202:
|
|
||||||
description: Tx was send and will probably be added to the next block
|
|
||||||
400:
|
|
||||||
description: The Tx was malformated
|
|
||||||
/blocks/latest:
|
|
||||||
get:
|
|
||||||
summary: Get the latest block
|
|
||||||
tags:
|
|
||||||
- query
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: The latest block
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Block"
|
|
||||||
/blocks/{height}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: height
|
|
||||||
description: Block height
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
get:
|
|
||||||
summary: Get a block at a certain height
|
|
||||||
tags:
|
|
||||||
- query
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: The block at a specific height
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Block"
|
|
||||||
404:
|
|
||||||
description: Block at height is not available
|
|
||||||
/validatorsets/latest:
|
|
||||||
get:
|
|
||||||
summary: Get the latest validator set
|
|
||||||
tags:
|
|
||||||
- validator set
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: The validator set at the latest block height
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
block_height:
|
|
||||||
type: number
|
|
||||||
validators:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Validator"
|
|
||||||
/validatorsets/{height}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: height
|
|
||||||
description: Block height
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
get:
|
|
||||||
summary: Get a validator set a certain height
|
|
||||||
tags:
|
|
||||||
- validator set
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: The validator set at a specific block height
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
block_height:
|
|
||||||
type: number
|
|
||||||
validators:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Validator"
|
|
||||||
404:
|
|
||||||
description: Block at height not available
|
|
||||||
# /txs:
|
|
||||||
# parameters:
|
|
||||||
# - in: query
|
|
||||||
# name: tag
|
|
||||||
# schema:
|
|
||||||
# type: string
|
|
||||||
# example: "coin.sender=EE5F3404034C524501629B56E0DDC38FAD651F04"
|
|
||||||
# required: true
|
|
||||||
# - in: query
|
|
||||||
# name: page
|
|
||||||
# description: Pagination page
|
|
||||||
# schema:
|
|
||||||
# type: number
|
|
||||||
# default: 0
|
|
||||||
# - in: query
|
|
||||||
# name: size
|
|
||||||
# description: Pagination size
|
|
||||||
# schema:
|
|
||||||
# type: number
|
|
||||||
# default: 50
|
|
||||||
# get:
|
|
||||||
# summary: Query Tx
|
|
||||||
# responses:
|
|
||||||
# 200:
|
|
||||||
# description: All Tx matching the provided tags
|
|
||||||
# content:
|
|
||||||
# application/json:
|
|
||||||
# schema:
|
|
||||||
# type: array
|
|
||||||
# items:
|
|
||||||
# $ref: "#/definitions/Tx"
|
|
||||||
# 404:
|
|
||||||
# description: Pagination is out of bounds
|
|
||||||
# /txs/sign:
|
|
||||||
# post:
|
|
||||||
# summary: Sign a Tx
|
|
||||||
# description: Sign a Tx providing locally stored account and according password
|
|
||||||
# security:
|
|
||||||
# - sign: []
|
|
||||||
# requestBody:
|
|
||||||
# content:
|
|
||||||
# application/json:
|
|
||||||
# schema:
|
|
||||||
# $ref: "#/definitions/TxBuild"
|
|
||||||
# responses:
|
|
||||||
# 200:
|
|
||||||
# description: The signed Tx
|
|
||||||
# content:
|
|
||||||
# application/json:
|
|
||||||
# schema:
|
|
||||||
# $ref: "#/definitions/TxSigned"
|
|
||||||
# 401:
|
|
||||||
# description: Account name and/or password where wrong
|
|
||||||
# /txs/broadcast:
|
|
||||||
# post:
|
|
||||||
# summary: Send signed Tx
|
|
||||||
# requestBody:
|
|
||||||
# content:
|
|
||||||
# application/json:
|
|
||||||
# schema:
|
|
||||||
# $ref: "#/definitions/TxSigned"
|
|
||||||
# responses:
|
|
||||||
# 202:
|
|
||||||
# description: Tx was send and will probably be added to the next block
|
|
||||||
# 400:
|
|
||||||
# description: The Tx was malformated
|
|
||||||
/txs/{hash}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: hash
|
|
||||||
description: Tx hash
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Get a Tx by hash
|
|
||||||
tags:
|
|
||||||
- query
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Tx with the provided hash
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
404:
|
|
||||||
description: Tx not available for provided hash
|
|
||||||
|
|
||||||
# ================== Staking Module # ==================
|
|
||||||
|
|
||||||
# TODO create D
|
|
||||||
/stake/delegators/{delegatorAddr}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Get all delegations (delegation, undelegation) from a delegator
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/validators:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: Bech32 AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Query all validators that a delegator is bonded to
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/validators/{validatorAddr}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: Bech32 AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: validatorAddr
|
|
||||||
description: Bech32 ValAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Query a validator that a delegator is bonded to
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/txs:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Get all staking txs (i.e msgs) from a delegator
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/delegations:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: Bech32 AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
post:
|
|
||||||
summary: Submit delegation
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: delegation
|
|
||||||
description: The password of the account to remove from the KMS
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
account_number:
|
|
||||||
type: number
|
|
||||||
delegations:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
begin_unbondings:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
complete_unbondings:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
begin_redelegates:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
complete_redelegates:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
chain_id:
|
|
||||||
type: string
|
|
||||||
gas:
|
|
||||||
type: number
|
|
||||||
sequence:
|
|
||||||
type: number
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: Bech32 AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: validatorAddr
|
|
||||||
description: Bech32 ValAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Query the current delegation status between a delegator and a validator
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
|
|
||||||
/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: delegatorAddr
|
|
||||||
description: Bech32 AccAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: validatorAddr
|
|
||||||
description: Bech32 ValAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Query all unbonding delegations between a delegator and a validator
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
/stake/validators:
|
|
||||||
get:
|
|
||||||
summary: Get all validator candidates
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
/stake/validators/{validatorAddr}:
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: validatorAddr
|
|
||||||
description: Bech32 ValAddress of Delegator
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
get:
|
|
||||||
summary: Query the information from a single validator
|
|
||||||
tags:
|
|
||||||
- stake
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
404:
|
|
||||||
description: Not Found
|
|
||||||
500:
|
|
||||||
description: Internal Server Error
|
|
||||||
|
|
||||||
# TODO Add staking definitions
|
|
||||||
definitions:
|
|
||||||
Address:
|
|
||||||
type: string
|
|
||||||
description: bech32 encoded addres
|
|
||||||
example: cosmos:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
|
||||||
ValidatorAddress:
|
|
||||||
type: string
|
|
||||||
description: bech32 encoded addres
|
|
||||||
example: cosmosvaloper:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
|
||||||
PubKey:
|
|
||||||
type: string
|
|
||||||
description: bech32 encoded public key
|
|
||||||
example: cosmospub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
|
||||||
ValidatorPubKey:
|
|
||||||
type: string
|
|
||||||
description: bech32 encoded public key
|
|
||||||
example: cosmosvalconspub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
|
||||||
Coins:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
denom:
|
|
||||||
type: string
|
|
||||||
example: steak
|
|
||||||
amount:
|
|
||||||
type: number
|
|
||||||
example: 50
|
|
||||||
Hash:
|
|
||||||
type: string
|
|
||||||
example: EE5F3404034C524501629B56E0DDC38FAD651F04
|
|
||||||
Tx:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- stake/delegate
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
TxChain:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
default: chain/tx
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
chain_id:
|
|
||||||
type: string
|
|
||||||
example: gaia-2
|
|
||||||
expires_at:
|
|
||||||
type: number
|
|
||||||
example: 0
|
|
||||||
tx:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
default: nonce
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
sequence:
|
|
||||||
type: number
|
|
||||||
example: 0
|
|
||||||
signers:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
chain:
|
|
||||||
type: string
|
|
||||||
example: ''
|
|
||||||
app:
|
|
||||||
type: string
|
|
||||||
default: sigs
|
|
||||||
addr:
|
|
||||||
$ref: "#/definitions/Address"
|
|
||||||
tx:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
TxBuild:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
default: sigs/one
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
tx:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
signature:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
Sig:
|
|
||||||
type: string
|
|
||||||
default: ''
|
|
||||||
Pubkey:
|
|
||||||
type: string
|
|
||||||
default: ''
|
|
||||||
TxSigned:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
default: sigs/one
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
tx:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
signature:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
Sig:
|
|
||||||
type: string
|
|
||||||
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
|
|
||||||
Pubkey:
|
|
||||||
$ref: "#/definitions/PubKey"
|
|
||||||
Account:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
example: Main Account
|
|
||||||
address:
|
|
||||||
$ref: "#/definitions/Address"
|
|
||||||
pub_key:
|
|
||||||
$ref: "#/definitions/PubKey"
|
|
||||||
Balance:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
height:
|
|
||||||
type: number
|
|
||||||
example: 123456
|
|
||||||
coins:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Coins"
|
|
||||||
credit:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
BlockID:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
parts:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
total:
|
|
||||||
type: number
|
|
||||||
example: 0
|
|
||||||
hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
Block:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
header:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
chain_id:
|
|
||||||
type: string
|
|
||||||
example: gaia-2
|
|
||||||
height:
|
|
||||||
type: number
|
|
||||||
example: 1
|
|
||||||
time:
|
|
||||||
type: string
|
|
||||||
example: '2017-12-30T05:53:09.287+01:00'
|
|
||||||
num_txs:
|
|
||||||
type: number
|
|
||||||
example: 0
|
|
||||||
last_block_id:
|
|
||||||
$ref: "#/definitions/BlockID"
|
|
||||||
total_txs:
|
|
||||||
type: number
|
|
||||||
example: 35
|
|
||||||
last_commit_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
data_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
validators_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
consensus_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
app_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
last_results_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
evidence_hash:
|
|
||||||
$ref: "#/definitions/Hash"
|
|
||||||
txs:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/definitions/Tx"
|
|
||||||
evidence:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
last_commit:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
blockID:
|
|
||||||
$ref: "#/definitions/BlockID"
|
|
||||||
precommits:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
Validator:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
address:
|
|
||||||
$ref: '#/definitions/ValidatorAddress'
|
|
||||||
pub_key:
|
|
||||||
$ref: "#/definitions/ValidatorPubKey"
|
|
||||||
power:
|
|
||||||
type: number
|
|
||||||
example: 1000
|
|
||||||
accum:
|
|
||||||
type: number
|
|
||||||
example: 1000
|
|
||||||
# Added by API Auto Mocking Plugin
|
|
||||||
host: virtserver.swaggerhub.com
|
|
||||||
basePath: /faboweb1/Cosmos-LCD-2/1.0.0
|
|
||||||
schemes:
|
|
||||||
- https
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Install the SDK
|
# Install the SDK
|
||||||
|
|
||||||
This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](full-node.md) or a [Validator](/validators/validator-setup.md).
|
This guide will explain how to install the [Cosmos SDK](/sdk/overview.md) onto your system. With the SDK installed on a server, you can participate in the latest testnet as either a [Full Node](./join-testnet.md#run-a-full-node) or a [Validator](/validators/validator-setup.md).
|
||||||
|
|
||||||
## Install Go
|
## Install Go
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ From the [networks/local directory](https://github.com/cosmos/cosmos-sdk/tree/de
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- [Install gaia](https://cosmos.network/docs/getting-started/installation.html)
|
- [Install gaia](./installation.md)
|
||||||
- [Install docker](https://docs.docker.com/engine/installation/)
|
- [Install docker](https://docs.docker.com/engine/installation/)
|
||||||
- [Install docker-compose](https://docs.docker.com/compose/install/)
|
- [Install docker-compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
## Tendermint and Cosmos
|
# Tendermint and Cosmos
|
||||||
|
|
||||||
Blockchains can be divided into three conceptual layers:
|
Blockchains can be divided into three conceptual layers:
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ The ability to tolerate machines failing in arbitrary ways, including becoming m
|
||||||
|
|
||||||
## Application Blockchain Interface
|
## Application Blockchain Interface
|
||||||
|
|
||||||
Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html#abci-overview) for a deep dive into the ABCI.
|
Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.com/docs/introduction/introduction.html#abci-overview) for a deep dive into the ABCI.
|
||||||
|
|
||||||
## Understanding the roles of the different layers
|
## Understanding the roles of the different layers
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ LCD will be used in the Cosmos Hub, the first Hub in the Cosmos network.
|
||||||
2. [**Get Started**](getting_started.md)
|
2. [**Get Started**](getting_started.md)
|
||||||
3. [**API**](api.md)
|
3. [**API**](api.md)
|
||||||
4. [**Specifications**](specification.md)
|
4. [**Specifications**](specification.md)
|
||||||
|
4. [**Update API docs To Swagger-UI**](update_API_docs.md)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ gaiacli tx broadcast --node=<node> signedSendTx.json
|
||||||
|
|
||||||
#### Set up a Validator
|
#### Set up a Validator
|
||||||
|
|
||||||
Please refer to the [Validator Setup](https://cosmos.network/docs/validators/validator-setup.html) section for a more complete guide on how to set up a validator-candidate.
|
Please refer to the [Validator Setup](../validators/validator-setup.md) section for a more complete guide on how to set up a validator-candidate.
|
||||||
|
|
||||||
#### Delegate to a Validator
|
#### Delegate to a Validator
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
## Application CLI
|
|
||||||
|
|
||||||
**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)**
|
|
||||||
|
|
||||||
To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// cmd/simplegovcli/main.go
|
|
||||||
...
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
client.GetCommands(
|
|
||||||
simplegovcmd.GetCmdQueryProposal("proposals", cdc),
|
|
||||||
simplegovcmd.GetCmdQueryProposals("proposals", cdc),
|
|
||||||
simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc),
|
|
||||||
simplegovcmd.GetCmdQueryProposalVote("proposals", cdc),
|
|
||||||
)...)
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
client.PostCommands(
|
|
||||||
simplegovcmd.PostCmdPropose(cdc),
|
|
||||||
simplegovcmd.PostCmdVote(cdc),
|
|
||||||
)...)
|
|
||||||
...
|
|
||||||
```
|
|
|
@ -1,21 +0,0 @@
|
||||||
## Application codec
|
|
||||||
|
|
||||||
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
|
|
||||||
|
|
||||||
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func MakeCodec() *codec.Codec {
|
|
||||||
var cdc = codec.New()
|
|
||||||
codec.RegisterCrypto(cdc) // Register crypto.
|
|
||||||
sdk.RegisterCodec(cdc) // Register Msgs
|
|
||||||
bank.RegisterCodec(cdc)
|
|
||||||
simplestake.RegisterCodec(cdc)
|
|
||||||
simpleGov.RegisterCodec(cdc)
|
|
||||||
|
|
||||||
// Register AppAccount
|
|
||||||
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
|
||||||
cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil)
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,9 +0,0 @@
|
||||||
## App commands
|
|
||||||
|
|
||||||
We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
// At root level of directory
|
|
||||||
cd cmd
|
|
||||||
```
|
|
||||||
`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application.
|
|
|
@ -1,61 +0,0 @@
|
||||||
## Application constructor
|
|
||||||
|
|
||||||
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
|
|
||||||
|
|
||||||
Now, we need to define the constructor for our application.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp
|
|
||||||
```
|
|
||||||
|
|
||||||
In this function, we will:
|
|
||||||
|
|
||||||
- Create the codec
|
|
||||||
|
|
||||||
```go
|
|
||||||
var cdc = MakeCodec()
|
|
||||||
```
|
|
||||||
|
|
||||||
- Instantiate our application. This includes creating the keys to access each of the substores.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create your application object.
|
|
||||||
var app = &SimpleGovApp{
|
|
||||||
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
|
||||||
cdc: cdc,
|
|
||||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
|
||||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
|
||||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
|
||||||
capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it.
|
|
||||||
|
|
||||||
```go
|
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
|
|
||||||
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace))
|
|
||||||
app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Declare the handlers.
|
|
||||||
|
|
||||||
```go
|
|
||||||
app.Router().
|
|
||||||
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
|
||||||
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)).
|
|
||||||
AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Initialize the application.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Initialize BaseApp.
|
|
||||||
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore)
|
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
|
||||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
return app
|
|
||||||
```
|
|
|
@ -1,4 +1,4 @@
|
||||||
## Application initialization
|
# Application Initialization
|
||||||
|
|
||||||
In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app.
|
In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app.
|
||||||
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
## Makefile
|
|
||||||
|
|
||||||
The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it:
|
|
||||||
|
|
||||||
```
|
|
||||||
// Makefile
|
|
||||||
build_examples:
|
|
||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
...
|
|
||||||
go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd
|
|
||||||
go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli
|
|
||||||
else
|
|
||||||
...
|
|
||||||
go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd
|
|
||||||
go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli
|
|
||||||
endif
|
|
||||||
...
|
|
||||||
install_examples:
|
|
||||||
...
|
|
||||||
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd
|
|
||||||
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli
|
|
||||||
```
|
|
|
@ -1,57 +0,0 @@
|
||||||
##### Rest server
|
|
||||||
|
|
||||||
**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)**
|
|
||||||
|
|
||||||
The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// cmd/simplegovd/main.go
|
|
||||||
// SimpleGovAppInit initial parameters
|
|
||||||
var SimpleGovAppInit = server.AppInit{
|
|
||||||
AppGenState: SimpleGovAppGenState,
|
|
||||||
AppGenTx: server.SimpleAppGenTx,
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
|
|
||||||
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
|
||||||
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
|
||||||
return app.NewSimpleGovApp(logger, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
|
||||||
dapp := app.NewSimpleGovApp(logger, db)
|
|
||||||
return dapp.ExportAppStateJSON()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, let us define the command for the daemon server within the `main()` function:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// cmd/simplegovd/main.go
|
|
||||||
func main() {
|
|
||||||
cdc := app.MakeCodec()
|
|
||||||
ctx := server.NewDefaultContext()
|
|
||||||
|
|
||||||
rootCmd := &cobra.Command{
|
|
||||||
Use: "simplegovd",
|
|
||||||
Short: "Simple Governance Daemon (server)",
|
|
||||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
|
||||||
}
|
|
||||||
|
|
||||||
server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit,
|
|
||||||
server.ConstructAppCreator(newApp, "simplegov"),
|
|
||||||
server.ConstructAppExporter(exportAppState, "simplegov"))
|
|
||||||
|
|
||||||
// prepare and add flags
|
|
||||||
rootDir := os.ExpandEnv("$HOME/.simplegovd")
|
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
|
||||||
executor.Execute()
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,55 +0,0 @@
|
||||||
## Application structure
|
|
||||||
|
|
||||||
Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory.
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
|
||||||
// At root level of directory
|
|
||||||
cd app
|
|
||||||
```
|
|
||||||
|
|
||||||
We are ready to create our simple governance application!
|
|
||||||
|
|
||||||
*Note: You can check the full file (with comments!) [here](link)*
|
|
||||||
|
|
||||||
The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed.
|
|
||||||
|
|
||||||
Secondly, we need to define the name of our application.
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
appName = "SimpleGovApp"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, let us define the structure of our application.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Extended ABCI application
|
|
||||||
type SimpleGovApp struct {
|
|
||||||
*bam.BaseApp
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
// keys to access the substores
|
|
||||||
capKeyMainStore *sdk.KVStoreKey
|
|
||||||
capKeyAccountStore *sdk.KVStoreKey
|
|
||||||
capKeyStakingStore *sdk.KVStoreKey
|
|
||||||
capKeySimpleGovStore *sdk.KVStoreKey
|
|
||||||
|
|
||||||
// keepers
|
|
||||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
|
||||||
bankKeeper bank.Keeper
|
|
||||||
stakeKeeper simplestake.Keeper
|
|
||||||
simpleGovKeeper simpleGov.Keeper
|
|
||||||
|
|
||||||
// Manage getting and setting accounts
|
|
||||||
accountMapper auth.AccountMapper
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Each application builds on top of the `BaseApp` template, hence the pointer.
|
|
||||||
- `cdc` is the codec used in our application.
|
|
||||||
- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store.
|
|
||||||
- Then come the keepers and mappers.
|
|
||||||
|
|
||||||
Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use.
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
# From Module To Application
|
||||||
|
|
||||||
|
## Application structure
|
||||||
|
|
||||||
|
Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// At root level of directory
|
||||||
|
cd app
|
||||||
|
```
|
||||||
|
|
||||||
|
We are ready to create our simple governance application!
|
||||||
|
|
||||||
|
*Note: You can check the full file (with comments!) [here](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)*
|
||||||
|
|
||||||
|
The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed.
|
||||||
|
|
||||||
|
Secondly, we need to define the name of our application.
|
||||||
|
|
||||||
|
```go
|
||||||
|
const (
|
||||||
|
appName = "SimpleGovApp"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, let us define the structure of our application.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Extended ABCI application
|
||||||
|
type SimpleGovApp struct {
|
||||||
|
*bam.BaseApp
|
||||||
|
cdc *codec.Codec
|
||||||
|
|
||||||
|
// keys to access the substores
|
||||||
|
capKeyMainStore *sdk.KVStoreKey
|
||||||
|
capKeyAccountStore *sdk.KVStoreKey
|
||||||
|
capKeyStakingStore *sdk.KVStoreKey
|
||||||
|
capKeySimpleGovStore *sdk.KVStoreKey
|
||||||
|
|
||||||
|
// keepers
|
||||||
|
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||||
|
bankKeeper bank.Keeper
|
||||||
|
stakeKeeper simplestake.Keeper
|
||||||
|
simpleGovKeeper simpleGov.Keeper
|
||||||
|
|
||||||
|
// Manage getting and setting accounts
|
||||||
|
accountMapper auth.AccountMapper
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Each application builds on top of the `BaseApp` template, hence the pointer.
|
||||||
|
- `cdc` is the codec used in our application.
|
||||||
|
- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store.
|
||||||
|
- Then come the keepers and mappers.
|
||||||
|
|
||||||
|
Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use.
|
||||||
|
|
||||||
|
## CLI and Rest server
|
||||||
|
|
||||||
|
We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// At root level of directory
|
||||||
|
cd cmd
|
||||||
|
```
|
||||||
|
`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application.
|
||||||
|
|
||||||
|
### Application CLI
|
||||||
|
|
||||||
|
**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)**
|
||||||
|
|
||||||
|
To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/simplegovcli/main.go
|
||||||
|
...
|
||||||
|
rootCmd.AddCommand(
|
||||||
|
client.GetCommands(
|
||||||
|
simplegovcmd.GetCmdQueryProposal("proposals", cdc),
|
||||||
|
simplegovcmd.GetCmdQueryProposals("proposals", cdc),
|
||||||
|
simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc),
|
||||||
|
simplegovcmd.GetCmdQueryProposalVote("proposals", cdc),
|
||||||
|
)...)
|
||||||
|
rootCmd.AddCommand(
|
||||||
|
client.PostCommands(
|
||||||
|
simplegovcmd.PostCmdPropose(cdc),
|
||||||
|
simplegovcmd.PostCmdVote(cdc),
|
||||||
|
)...)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rest server
|
||||||
|
|
||||||
|
**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)**
|
||||||
|
|
||||||
|
The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/simplegovd/main.go
|
||||||
|
// SimpleGovAppInit initial parameters
|
||||||
|
var SimpleGovAppInit = server.AppInit{
|
||||||
|
AppGenState: SimpleGovAppGenState,
|
||||||
|
AppGenTx: server.SimpleAppGenTx,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
|
||||||
|
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||||
|
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||||
|
return app.NewSimpleGovApp(logger, db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||||
|
dapp := app.NewSimpleGovApp(logger, db)
|
||||||
|
return dapp.ExportAppStateJSON()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, let us define the command for the daemon server within the `main()` function:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// cmd/simplegovd/main.go
|
||||||
|
func main() {
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
ctx := server.NewDefaultContext()
|
||||||
|
|
||||||
|
rootCmd := &cobra.Command{
|
||||||
|
Use: "simplegovd",
|
||||||
|
Short: "Simple Governance Daemon (server)",
|
||||||
|
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit,
|
||||||
|
server.ConstructAppCreator(newApp, "simplegov"),
|
||||||
|
server.ConstructAppExporter(exportAppState, "simplegov"))
|
||||||
|
|
||||||
|
// prepare and add flags
|
||||||
|
rootDir := os.ExpandEnv("$HOME/.simplegovd")
|
||||||
|
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||||
|
executor.Execute()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Makefile
|
||||||
|
|
||||||
|
The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it:
|
||||||
|
|
||||||
|
```
|
||||||
|
// Makefile
|
||||||
|
build_examples:
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
...
|
||||||
|
go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd
|
||||||
|
go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli
|
||||||
|
else
|
||||||
|
...
|
||||||
|
go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd
|
||||||
|
go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli
|
||||||
|
endif
|
||||||
|
...
|
||||||
|
install_examples:
|
||||||
|
...
|
||||||
|
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd
|
||||||
|
go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Application constructor
|
||||||
|
|
||||||
|
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
|
||||||
|
|
||||||
|
Now, we need to define the constructor for our application.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp
|
||||||
|
```
|
||||||
|
|
||||||
|
In this function, we will:
|
||||||
|
|
||||||
|
- Create the codec
|
||||||
|
|
||||||
|
```go
|
||||||
|
var cdc = MakeCodec()
|
||||||
|
```
|
||||||
|
|
||||||
|
- Instantiate our application. This includes creating the keys to access each of the substores.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create your application object.
|
||||||
|
var app = &SimpleGovApp{
|
||||||
|
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
||||||
|
cdc: cdc,
|
||||||
|
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||||
|
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||||
|
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||||
|
capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it.
|
||||||
|
|
||||||
|
```go
|
||||||
|
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
|
||||||
|
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper,app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||||
|
app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace))
|
||||||
|
```
|
||||||
|
|
||||||
|
- Declare the handlers.
|
||||||
|
|
||||||
|
```go
|
||||||
|
app.Router().
|
||||||
|
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
||||||
|
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)).
|
||||||
|
AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper))
|
||||||
|
```
|
||||||
|
|
||||||
|
- Initialize the application.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Initialize BaseApp.
|
||||||
|
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore)
|
||||||
|
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
||||||
|
err := app.LoadLatestVersion(app.capKeyMainStore)
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit(err.Error())
|
||||||
|
}
|
||||||
|
return app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Application codec
|
||||||
|
|
||||||
|
**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)**
|
||||||
|
|
||||||
|
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MakeCodec() *codec.Codec {
|
||||||
|
var cdc = codec.New()
|
||||||
|
codec.RegisterCrypto(cdc) // Register crypto.
|
||||||
|
sdk.RegisterCodec(cdc) // Register Msgs
|
||||||
|
bank.RegisterCodec(cdc)
|
||||||
|
simplestake.RegisterCodec(cdc)
|
||||||
|
simpleGov.RegisterCodec(cdc)
|
||||||
|
|
||||||
|
// Register AppAccount
|
||||||
|
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil)
|
||||||
|
return cdc
|
||||||
|
}
|
||||||
|
```
|
|
@ -1,19 +0,0 @@
|
||||||
## Cast a vote to an existing proposal
|
|
||||||
|
|
||||||
Let's cast a vote on the created proposal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli vote --proposal-id=1 --option="No"
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the value of the option from your casted vote :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli proposal-vote 1 <your_address>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also check all the casted votes of a proposal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli proposals-votes 1
|
|
||||||
```
|
|
|
@ -6,45 +6,43 @@ Before getting in the bulk of the code, we will start by some introductory conte
|
||||||
|
|
||||||
## Table of contents:
|
## Table of contents:
|
||||||
|
|
||||||
### Introduction - Prerequsite reading
|
### Introduction - Prerequisite reading
|
||||||
|
|
||||||
- [Intro to Tendermint and Cosmos](../../../introduction/tendermint-cosmos.md)
|
- [Intro to Tendermint and Cosmos](/introduction/tendermint-cosmos.md)
|
||||||
- [Tendermint Core and ABCI](../../../introduction/tendermint.md)
|
- [Tendermint Core and ABCI](/introduction/tendermint.md)
|
||||||
- [Intro to Cosmos-SDK](../../overview.md)
|
- [Intro to Cosmos-SDK](/sdk/overview.md)
|
||||||
- [Starting your own project](start.md)
|
|
||||||
|
|
||||||
### Setup and design phase
|
|
||||||
|
|
||||||
- [Setup](setup.md)
|
### [Setup and design](setup-and-design.md)
|
||||||
- [Application design](app-design.md)
|
|
||||||
|
- [Starting your own project](setup-and-design.md#get-started)
|
||||||
|
- [Setup](setup-and-design.md#setup)
|
||||||
|
- [Application design](setup-and-design.md#application-design)
|
||||||
|
|
||||||
### Implementation of the application
|
### Implementation of the application
|
||||||
|
|
||||||
**Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details**
|
**Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details**
|
||||||
|
|
||||||
- [Application initialization](app-init.md)
|
- [Application initialization](app-init.md)
|
||||||
- Simple Governance module
|
- [Simple Governance module](simple-gov-module.md)
|
||||||
+ [Module initialization](module-init.md)
|
+ [Module initialization](simple-gov-module.md#module-initialization)
|
||||||
+ [Types](module-types.md)
|
+ [Types](simple-gov-module.md#types)
|
||||||
+ [Keeper](module-keeper.md)
|
+ [Keeper](simple-gov-module.md#keeper)
|
||||||
+ [Handler](module-handler.md)
|
+ [Handler](simple-gov-module.md#handler)
|
||||||
+ [Wire](module-codec.md)
|
+ [Wire](simple-gov-module.md#codec)
|
||||||
+ [Errors](module-errors.md)
|
+ [Errors](simple-gov-module.md#errors)
|
||||||
+ Command-Line Interface and Rest API
|
+ [Command-Line Interface](simple-gov-module.md#command-line-interface)
|
||||||
* [Command-Line Interface](module-cli.md)
|
+ [Rest API](simple-gov-module.md#rest-api)
|
||||||
* [Rest API](module-rest.md)
|
- [Bridging it all together](bridging-it-all.md)
|
||||||
- Bridging it all together
|
+ [Application structure](bridging-it-all.md#application-structure)
|
||||||
+ [Application structure](app-structure.md)
|
+ [Application CLI and Rest Server](bridging-it-all.md#cli-and-rest-server)
|
||||||
+ [Application CLI and Rest Server](app-commands.md)
|
+ [Makefile](bridging-it-all.md#makefile)
|
||||||
* [Application CLI](app-cli.md)
|
+ [Application constructor](bridging-it-all.md#application-constructor)
|
||||||
* [Rest Server](app-rest.md)
|
+ [Application codec](bridging-it-all.md#application-codec)
|
||||||
+ [Makefile](app-makefile.md)
|
- [Running the application](running-the-application.md)
|
||||||
+ [Application constructor](app-constructor.md)
|
+ [Installation](running-the-application.md#installation)
|
||||||
+ [Application codec](app-codec.md)
|
+ [Submit a proposal](running-the-application.md#submit-a-proposal)
|
||||||
- Running the application
|
+ [Cast a vote](running-the-application.md#cast-a-vote)
|
||||||
+ [Installation](run-install.md)
|
|
||||||
+ [Submit a proposal](submit-proposal.md)
|
|
||||||
+ [Cast a vote](cast-vote.md)
|
|
||||||
|
|
||||||
## Useful links
|
## Useful links
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
## Command-Line Interface (CLI)
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)**
|
|
||||||
|
|
||||||
Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module.
|
|
||||||
|
|
||||||
The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Declare flags
|
|
||||||
const(
|
|
||||||
Flag = "flag"
|
|
||||||
...
|
|
||||||
)
|
|
||||||
|
|
||||||
// Main command function. One function for each command.
|
|
||||||
func Command(codec *codec.Codec) *cobra.Command {
|
|
||||||
// Create the command to return
|
|
||||||
command := &cobra.Command{
|
|
||||||
Use: "actual command",
|
|
||||||
Short: "Short description",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// Actual function to run when command is used
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add flags to the command
|
|
||||||
command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>")
|
|
||||||
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
## Codec
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)**
|
|
||||||
|
|
||||||
The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
|
||||||
cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil)
|
|
||||||
cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more).
|
|
|
@ -1,7 +0,0 @@
|
||||||
## Errors
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)**
|
|
||||||
|
|
||||||
The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory.
|
|
||||||
|
|
||||||
Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result.
|
|
|
@ -1,73 +0,0 @@
|
||||||
## Handler
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)**
|
|
||||||
|
|
||||||
### Constructor and core handlers
|
|
||||||
|
|
||||||
Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function.
|
|
||||||
|
|
||||||
In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewHandler(k Keeper) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case SubmitProposalMsg:
|
|
||||||
return handleSubmitProposalMsg(ctx, k, msg)
|
|
||||||
case VoteMsg:
|
|
||||||
return handleVoteMsg(ctx, k, msg)
|
|
||||||
default:
|
|
||||||
errMsg := "Unrecognized gov Msg type: " + msg.Name()
|
|
||||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result
|
|
||||||
```
|
|
||||||
|
|
||||||
Let us take a look at the parameters of this function:
|
|
||||||
|
|
||||||
- The context `ctx` to access the stores.
|
|
||||||
- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case).
|
|
||||||
- The message `msg` that holds all the information provided by the sender of the transaction.
|
|
||||||
|
|
||||||
The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info.
|
|
||||||
|
|
||||||
### BeginBlocker and EndBlocker
|
|
||||||
|
|
||||||
In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications.
|
|
||||||
|
|
||||||
This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether.
|
|
||||||
|
|
||||||
`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern):
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
|
||||||
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
|
||||||
err := checkProposal(ctx, k)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md).
|
|
||||||
|
|
||||||
For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed:
|
|
||||||
|
|
||||||
1. Get the oldest proposal from the `ProposalProcessingQueue`
|
|
||||||
2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit.
|
|
||||||
3. Check if proposal is accepted or rejected. Update the proposal status.
|
|
||||||
4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1.
|
|
||||||
|
|
||||||
Let us perform a quick safety analysis on this process.
|
|
||||||
- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite
|
|
||||||
- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue.
|
|
||||||
- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`.
|
|
|
@ -1,31 +0,0 @@
|
||||||
## Module initialization
|
|
||||||
|
|
||||||
First, let us go into the module's folder and create a folder for our module.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd x/
|
|
||||||
mkdir simple_governance
|
|
||||||
cd simple_governance
|
|
||||||
mkdir -p client/cli client/rest
|
|
||||||
touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go
|
|
||||||
```
|
|
||||||
|
|
||||||
Let us start by adding the files we will need. Your module's folder should look something like that:
|
|
||||||
|
|
||||||
```
|
|
||||||
x
|
|
||||||
└─── simple_governance
|
|
||||||
├─── client
|
|
||||||
│ ├─── cli
|
|
||||||
│ │ └─── simple_governance.go
|
|
||||||
│ └─── rest
|
|
||||||
│ └─── simple_governance.go
|
|
||||||
├─── errors.go
|
|
||||||
├─── handler.go
|
|
||||||
├─── keeper_keys.go
|
|
||||||
├─── keeper.go
|
|
||||||
├─── types.go
|
|
||||||
└─── codec.go
|
|
||||||
```
|
|
||||||
|
|
||||||
Let us go into the detail of each of these files.
|
|
|
@ -1,96 +0,0 @@
|
||||||
## Keeper
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)**
|
|
||||||
|
|
||||||
### Short intro to keepers
|
|
||||||
|
|
||||||
`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos.
|
|
||||||
|
|
||||||
|
|
||||||
As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// in app.go
|
|
||||||
|
|
||||||
// instanciate keepers
|
|
||||||
keeperA = moduleA.newKeeper(app.moduleAStoreKey)
|
|
||||||
keeperB = moduleB.newKeeper(app.moduleBStoreKey)
|
|
||||||
|
|
||||||
// pass instance of keeperA to handler of module B
|
|
||||||
app.Router().
|
|
||||||
AddRoute("moduleA", moduleA.NewHandler(keeperA)).
|
|
||||||
AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance
|
|
||||||
```
|
|
||||||
|
|
||||||
`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper.
|
|
||||||
|
|
||||||
### Store for our app
|
|
||||||
|
|
||||||
Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them.
|
|
||||||
|
|
||||||
- `Proposals` will be indexed by `'proposals'|<proposalID>`.
|
|
||||||
- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`.
|
|
||||||
|
|
||||||
Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`.
|
|
||||||
|
|
||||||
These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`.
|
|
||||||
|
|
||||||
### Keepers for our app
|
|
||||||
|
|
||||||
In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use.
|
|
||||||
|
|
||||||
Now, let us try to think about which keeper from **external** modules our module's keepers need access to.
|
|
||||||
Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler.
|
|
||||||
|
|
||||||
With all that in mind, we can define the structure of our `Keeper`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Keeper struct {
|
|
||||||
SimpleGov sdk.StoreKey // Key to our module's store
|
|
||||||
cdc *codec.Codec // Codec to encore/decode structs
|
|
||||||
ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance
|
|
||||||
sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store.
|
|
||||||
codespace sdk.CodespaceType // Reserves space for error codes
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And the structure of our `KeeperRead`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type KeeperRead struct {
|
|
||||||
Keeper
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store.
|
|
||||||
|
|
||||||
### Functions and Methods
|
|
||||||
|
|
||||||
The first function we have to create is the constructor.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper
|
|
||||||
```
|
|
||||||
|
|
||||||
This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead
|
|
||||||
```
|
|
||||||
|
|
||||||
Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level.
|
|
||||||
|
|
||||||
*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers*
|
|
||||||
|
|
||||||
Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`.
|
|
||||||
|
|
||||||
- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read.
|
|
||||||
- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored.
|
|
||||||
- `NewProposalID`: A function to generate a new unique `proposalID`.
|
|
||||||
- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`.
|
|
||||||
- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`.
|
|
||||||
- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period.
|
|
||||||
|
|
||||||
The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error.
|
|
||||||
|
|
||||||
*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*.
|
|
|
@ -1,32 +0,0 @@
|
||||||
## Rest API
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)**
|
|
||||||
|
|
||||||
The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**.
|
|
||||||
|
|
||||||
________________________________________________________
|
|
||||||
|
|
||||||
USER INTERFACE <=======> REST SERVER <=======> FULL-NODE
|
|
||||||
|
|
||||||
________________________________________________________
|
|
||||||
|
|
||||||
It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`.
|
|
||||||
|
|
||||||
- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally.
|
|
||||||
|
|
||||||
- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally.
|
|
||||||
|
|
||||||
Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder.
|
|
||||||
|
|
||||||
| Method | URL | Description |
|
|
||||||
|--------|---------------------------------|-------------------------------------------------------------|
|
|
||||||
| GET | /proposals | Range query to get all submitted proposals |
|
|
||||||
| POST | /proposals | Submit a new proposal |
|
|
||||||
| GET | /proposals/{id} | Returns a proposal given its ID |
|
|
||||||
| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal |
|
|
||||||
| POST | /proposals/{id}/votes | Cast a vote on a given proposal |
|
|
||||||
| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal |
|
|
||||||
|
|
||||||
It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it.
|
|
||||||
|
|
||||||
Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices.
|
|
|
@ -1,23 +0,0 @@
|
||||||
## Types
|
|
||||||
|
|
||||||
**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)**
|
|
||||||
|
|
||||||
In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section.
|
|
||||||
|
|
||||||
For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in.
|
|
||||||
|
|
||||||
Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type:
|
|
||||||
|
|
||||||
- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method.
|
|
||||||
- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message.
|
|
||||||
- `GetSigners()`: Returns address(es) of the signer(s).
|
|
||||||
- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction.
|
|
||||||
- `Get()`: A basic getter, returns some property of the message.
|
|
||||||
- `String()`: Returns a human-readable version of the message
|
|
||||||
|
|
||||||
For our simple governance messages, this means:
|
|
||||||
|
|
||||||
- `Type()` will return `"simpleGov"`
|
|
||||||
- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit
|
|
||||||
- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative.
|
|
||||||
- As for other methods, less customization is required. You can check the code to see a standard way of implementing these.
|
|
|
@ -1,18 +0,0 @@
|
||||||
## Installation
|
|
||||||
|
|
||||||
Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/<your_username>/cosmos-sdk
|
|
||||||
cd $GOPATH/src/github.com/<your_username>/cosmos-sdk
|
|
||||||
make get_vendor_deps
|
|
||||||
make install
|
|
||||||
make install_examples
|
|
||||||
```
|
|
||||||
|
|
||||||
Check that the app is correctly installed by typing:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli -h
|
|
||||||
simplegovd -h
|
|
||||||
```
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Running The Application
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/<your_username>/cosmos-sdk
|
||||||
|
cd $GOPATH/src/github.com/<your_username>/cosmos-sdk
|
||||||
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
make install_examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that the app is correctly installed by typing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli -h
|
||||||
|
simplegovd -h
|
||||||
|
```
|
||||||
|
|
||||||
|
## Submit a proposal
|
||||||
|
|
||||||
|
Uuse the CLI to create a new proposal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, via a json file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli propose --proposal="path/to/proposal.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
Where proposal.json contains:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Voting Period Update",
|
||||||
|
"description": "Should we change the proposal voting period to 3 weeks?",
|
||||||
|
"type": "Text",
|
||||||
|
"deposit": "300Atoms"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Get the details of your newly created proposal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli proposal 1
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also check all the existing open proposals:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli proposals --active=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cast a vote
|
||||||
|
|
||||||
|
Let's cast a vote on the created proposal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli vote --proposal-id=1 --option="No"
|
||||||
|
```
|
||||||
|
|
||||||
|
Get the value of the option from your casted vote :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli proposal-vote 1 <your_address>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also check all the casted votes of a proposal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simplegovcli proposals-votes 1
|
||||||
|
```
|
|
@ -1,3 +1,47 @@
|
||||||
|
# Setup And Design
|
||||||
|
|
||||||
|
## Get started
|
||||||
|
|
||||||
|
To get started, you just have to follow these simple steps:
|
||||||
|
|
||||||
|
1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo
|
||||||
|
2. Code the modules needed by your application that do not already exist.
|
||||||
|
3. Create your app directory. In the app main file, import the module you need and instantiate the different stores.
|
||||||
|
4. Launch your blockchain.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed
|
||||||
|
- Don't forget to set your `PATH` and `GOPATH`
|
||||||
|
|
||||||
|
### Setup work environment
|
||||||
|
|
||||||
|
Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd $GOPATH/src/github.com/your_username
|
||||||
|
git clone github.com/your_username/cosmos-sdk
|
||||||
|
cd cosmos-sdk
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote add upstream github.com/cosmos/cosmos-sdk
|
||||||
|
git fetch upstream
|
||||||
|
git rebase upstream/master
|
||||||
|
```
|
||||||
|
|
||||||
|
We will also create a branch dedicated to our module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b my_new_application
|
||||||
|
```
|
||||||
|
|
||||||
|
We are all set!
|
||||||
|
|
||||||
## Application design
|
## Application design
|
||||||
|
|
||||||
### Application description
|
### Application description
|
|
@ -1,32 +0,0 @@
|
||||||
## Setup
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed
|
|
||||||
- Don't forget to set your `PATH` and `GOPATH`
|
|
||||||
|
|
||||||
### Setup work environment
|
|
||||||
|
|
||||||
Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd $GOPATH/src/github.com/your_username
|
|
||||||
git clone github.com/your_username/cosmos-sdk
|
|
||||||
cd cosmos-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git remote add upstream github.com/cosmos/cosmos-sdk
|
|
||||||
git fetch upstream
|
|
||||||
git rebase upstream/master
|
|
||||||
```
|
|
||||||
|
|
||||||
We will also create a branch dedicated to our module:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout -b my_new_application
|
|
||||||
```
|
|
||||||
|
|
||||||
We are all set!
|
|
|
@ -0,0 +1,316 @@
|
||||||
|
# Simple Governance Module
|
||||||
|
|
||||||
|
## Module initialization
|
||||||
|
|
||||||
|
First, let us go into the module's folder and create a folder for our module.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd x/
|
||||||
|
mkdir simple_governance
|
||||||
|
cd simple_governance
|
||||||
|
mkdir -p client/cli client/rest
|
||||||
|
touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Let us start by adding the files we will need. Your module's folder should look something like that:
|
||||||
|
|
||||||
|
```
|
||||||
|
x
|
||||||
|
└─── simple_governance
|
||||||
|
├─── client
|
||||||
|
│ ├─── cli
|
||||||
|
│ │ └─── simple_governance.go
|
||||||
|
│ └─── rest
|
||||||
|
│ └─── simple_governance.go
|
||||||
|
├─── errors.go
|
||||||
|
├─── handler.go
|
||||||
|
├─── keeper_keys.go
|
||||||
|
├─── keeper.go
|
||||||
|
├─── types.go
|
||||||
|
└─── codec.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Let us go into the detail of each of these files.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)**
|
||||||
|
|
||||||
|
In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section.
|
||||||
|
|
||||||
|
For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in.
|
||||||
|
|
||||||
|
Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type:
|
||||||
|
|
||||||
|
- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method.
|
||||||
|
- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message.
|
||||||
|
- `GetSigners()`: Returns address(es) of the signer(s).
|
||||||
|
- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction.
|
||||||
|
- `Get()`: A basic getter, returns some property of the message.
|
||||||
|
- `String()`: Returns a human-readable version of the message
|
||||||
|
|
||||||
|
For our simple governance messages, this means:
|
||||||
|
|
||||||
|
- `Type()` will return `"simpleGov"`
|
||||||
|
- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit
|
||||||
|
- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative.
|
||||||
|
- As for other methods, less customization is required. You can check the code to see a standard way of implementing these.
|
||||||
|
|
||||||
|
## Keeper
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)**
|
||||||
|
|
||||||
|
### Short intro to keepers
|
||||||
|
|
||||||
|
`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos.
|
||||||
|
|
||||||
|
|
||||||
|
As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// in app.go
|
||||||
|
|
||||||
|
// instanciate keepers
|
||||||
|
keeperA = moduleA.newKeeper(app.moduleAStoreKey)
|
||||||
|
keeperB = moduleB.newKeeper(app.moduleBStoreKey)
|
||||||
|
|
||||||
|
// pass instance of keeperA to handler of module B
|
||||||
|
app.Router().
|
||||||
|
AddRoute("moduleA", moduleA.NewHandler(keeperA)).
|
||||||
|
AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance
|
||||||
|
```
|
||||||
|
|
||||||
|
`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper.
|
||||||
|
|
||||||
|
### Store for our app
|
||||||
|
|
||||||
|
Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them.
|
||||||
|
|
||||||
|
- `Proposals` will be indexed by `'proposals'|<proposalID>`.
|
||||||
|
- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`.
|
||||||
|
|
||||||
|
Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`.
|
||||||
|
|
||||||
|
These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`.
|
||||||
|
|
||||||
|
### Keepers for our app
|
||||||
|
|
||||||
|
In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use.
|
||||||
|
|
||||||
|
Now, let us try to think about which keeper from **external** modules our module's keepers need access to.
|
||||||
|
Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler.
|
||||||
|
|
||||||
|
With all that in mind, we can define the structure of our `Keeper`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Keeper struct {
|
||||||
|
SimpleGov sdk.StoreKey // Key to our module's store
|
||||||
|
cdc *codec.Codec // Codec to encore/decode structs
|
||||||
|
ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance
|
||||||
|
sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store.
|
||||||
|
codespace sdk.CodespaceType // Reserves space for error codes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And the structure of our `KeeperRead`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type KeeperRead struct {
|
||||||
|
Keeper
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store.
|
||||||
|
|
||||||
|
### Functions and Methods
|
||||||
|
|
||||||
|
The first function we have to create is the constructor.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper
|
||||||
|
```
|
||||||
|
|
||||||
|
This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level.
|
||||||
|
|
||||||
|
*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers*
|
||||||
|
|
||||||
|
Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`.
|
||||||
|
|
||||||
|
- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read.
|
||||||
|
- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored.
|
||||||
|
- `NewProposalID`: A function to generate a new unique `proposalID`.
|
||||||
|
- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`.
|
||||||
|
- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`.
|
||||||
|
- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period.
|
||||||
|
|
||||||
|
The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error.
|
||||||
|
|
||||||
|
*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*.
|
||||||
|
|
||||||
|
## Handler
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)**
|
||||||
|
|
||||||
|
### Constructor and core handlers
|
||||||
|
|
||||||
|
Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function.
|
||||||
|
|
||||||
|
In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewHandler(k Keeper) sdk.Handler {
|
||||||
|
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case SubmitProposalMsg:
|
||||||
|
return handleSubmitProposalMsg(ctx, k, msg)
|
||||||
|
case VoteMsg:
|
||||||
|
return handleVoteMsg(ctx, k, msg)
|
||||||
|
default:
|
||||||
|
errMsg := "Unrecognized gov Msg type: " + reflect.TypeOf(msg).Name()
|
||||||
|
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result
|
||||||
|
```
|
||||||
|
|
||||||
|
Let us take a look at the parameters of this function:
|
||||||
|
|
||||||
|
- The context `ctx` to access the stores.
|
||||||
|
- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case).
|
||||||
|
- The message `msg` that holds all the information provided by the sender of the transaction.
|
||||||
|
|
||||||
|
The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info.
|
||||||
|
|
||||||
|
### BeginBlocker and EndBlocker
|
||||||
|
|
||||||
|
In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications.
|
||||||
|
|
||||||
|
This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether.
|
||||||
|
|
||||||
|
`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern):
|
||||||
|
|
||||||
|
```go
|
||||||
|
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||||
|
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||||
|
err := checkProposal(ctx, k)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md).
|
||||||
|
|
||||||
|
For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed:
|
||||||
|
|
||||||
|
1. Get the oldest proposal from the `ProposalProcessingQueue`
|
||||||
|
2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit.
|
||||||
|
3. Check if proposal is accepted or rejected. Update the proposal status.
|
||||||
|
4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1.
|
||||||
|
|
||||||
|
Let us perform a quick safety analysis on this process.
|
||||||
|
- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite
|
||||||
|
- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue.
|
||||||
|
- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`.
|
||||||
|
|
||||||
|
## Codec
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)**
|
||||||
|
|
||||||
|
The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RegisterCodec(cdc *codec.Codec) {
|
||||||
|
cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil)
|
||||||
|
cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more).
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)**
|
||||||
|
|
||||||
|
The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory.
|
||||||
|
|
||||||
|
Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result.
|
||||||
|
|
||||||
|
## Command-Line Interface
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)**
|
||||||
|
|
||||||
|
Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module.
|
||||||
|
|
||||||
|
The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Declare flags
|
||||||
|
const(
|
||||||
|
Flag = "flag"
|
||||||
|
...
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main command function. One function for each command.
|
||||||
|
func Command(codec *codec.Codec) *cobra.Command {
|
||||||
|
// Create the command to return
|
||||||
|
command := &cobra.Command{
|
||||||
|
Use: "actual command",
|
||||||
|
Short: "Short description",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// Actual function to run when command is used
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add flags to the command
|
||||||
|
command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>")
|
||||||
|
|
||||||
|
return command
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rest API
|
||||||
|
|
||||||
|
**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)**
|
||||||
|
|
||||||
|
The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**.
|
||||||
|
|
||||||
|
________________________________________________________
|
||||||
|
|
||||||
|
USER INTERFACE <=======> REST SERVER <=======> FULL-NODE
|
||||||
|
|
||||||
|
________________________________________________________
|
||||||
|
|
||||||
|
It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`.
|
||||||
|
|
||||||
|
- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally.
|
||||||
|
|
||||||
|
- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally.
|
||||||
|
|
||||||
|
Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder.
|
||||||
|
|
||||||
|
| Method | URL | Description |
|
||||||
|
|--------|---------------------------------|-------------------------------------------------------------|
|
||||||
|
| GET | /proposals | Range query to get all submitted proposals |
|
||||||
|
| POST | /proposals | Submit a new proposal |
|
||||||
|
| GET | /proposals/{id} | Returns a proposal given its ID |
|
||||||
|
| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal |
|
||||||
|
| POST | /proposals/{id}/votes | Cast a vote on a given proposal |
|
||||||
|
| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal |
|
||||||
|
|
||||||
|
It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it.
|
||||||
|
|
||||||
|
Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices.
|
|
@ -1,10 +0,0 @@
|
||||||
## Starting your own project
|
|
||||||
|
|
||||||
To get started, you just have to follow these simple steps:
|
|
||||||
|
|
||||||
1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo
|
|
||||||
2. Code the modules needed by your application that do not already exist.
|
|
||||||
3. Create your app directory. In the app main file, import the module you need and instantiate the different stores.
|
|
||||||
4. Launch your blockchain.
|
|
||||||
|
|
||||||
Easy as pie! With the introduction over, let us delve into practice and learn how to code a SDK application with an example.
|
|
|
@ -1,36 +0,0 @@
|
||||||
## Submit a proposal
|
|
||||||
|
|
||||||
Uuse the CLI to create a new proposal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, via a json file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli propose --proposal="path/to/proposal.json"
|
|
||||||
```
|
|
||||||
|
|
||||||
Where proposal.json contains:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Voting Period Update",
|
|
||||||
"description": "Should we change the proposal voting period to 3 weeks?",
|
|
||||||
"type": "Text",
|
|
||||||
"deposit": "300Atoms"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the details of your newly created proposal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli proposal 1
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also check all the existing open proposals:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
simplegovcli proposals --active=true
|
|
||||||
```
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
"github.com/cosmos/cosmos-sdk/client/lcd"
|
||||||
|
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
|
||||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||||
|
|
|
@ -12,6 +12,7 @@ INEFFASSIGN = github.com/gordonklaus/ineffassign
|
||||||
MISSPELL = github.com/client9/misspell/cmd/misspell
|
MISSPELL = github.com/client9/misspell/cmd/misspell
|
||||||
ERRCHECK = github.com/kisielk/errcheck
|
ERRCHECK = github.com/kisielk/errcheck
|
||||||
UNPARAM = mvdan.cc/unparam
|
UNPARAM = mvdan.cc/unparam
|
||||||
|
STATIK = github.com/rakyll/statik
|
||||||
|
|
||||||
DEP_CHECK := $(shell command -v dep 2> /dev/null)
|
DEP_CHECK := $(shell command -v dep 2> /dev/null)
|
||||||
GOLINT_CHECK := $(shell command -v golint 2> /dev/null)
|
GOLINT_CHECK := $(shell command -v golint 2> /dev/null)
|
||||||
|
@ -21,6 +22,7 @@ INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null)
|
||||||
MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null)
|
MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null)
|
||||||
ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null)
|
ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null)
|
||||||
UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null)
|
UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null)
|
||||||
|
STATIK_CHECK := $(shell command -v statik 2> /dev/null)
|
||||||
|
|
||||||
check_tools:
|
check_tools:
|
||||||
ifndef DEP_CHECK
|
ifndef DEP_CHECK
|
||||||
|
@ -66,6 +68,12 @@ ifndef UNPARAM_CHECK
|
||||||
else
|
else
|
||||||
@echo "Found unparam in path."
|
@echo "Found unparam in path."
|
||||||
endif
|
endif
|
||||||
|
ifndef STATIK_CHECK
|
||||||
|
@echo "No statik in path. Install with 'make get_tools'."
|
||||||
|
else
|
||||||
|
@echo "Found statik in path."
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
get_tools:
|
get_tools:
|
||||||
ifdef DEP_CHECK
|
ifdef DEP_CHECK
|
||||||
|
@ -74,6 +82,13 @@ else
|
||||||
@echo "Installing dep"
|
@echo "Installing dep"
|
||||||
go get -v $(DEP)
|
go get -v $(DEP)
|
||||||
endif
|
endif
|
||||||
|
ifdef STATIK_CHECK
|
||||||
|
@echo "Statik is already installed. Run 'make update_tools' to update."
|
||||||
|
else
|
||||||
|
@echo "Installing statik"
|
||||||
|
go version
|
||||||
|
go get -v $(STATIK)
|
||||||
|
endif
|
||||||
|
|
||||||
get_dev_tools:
|
get_dev_tools:
|
||||||
$(MAKE) get_tools
|
$(MAKE) get_tools
|
||||||
|
@ -119,6 +134,12 @@ else
|
||||||
@echo "Installing unparam"
|
@echo "Installing unparam"
|
||||||
go get -v $(UNPARAM)
|
go get -v $(UNPARAM)
|
||||||
endif
|
endif
|
||||||
|
ifdef STATIK_CHECK
|
||||||
|
@echo "statik is already installed. Run 'make update_tools' to update."
|
||||||
|
else
|
||||||
|
@echo "Installing statik"
|
||||||
|
go get -v $(STATIK)
|
||||||
|
endif
|
||||||
|
|
||||||
update_tools:
|
update_tools:
|
||||||
@echo "Updating dep"
|
@echo "Updating dep"
|
||||||
|
@ -140,6 +161,8 @@ update_dev_tools:
|
||||||
go get -u -v $(ERRCHECK)
|
go get -u -v $(ERRCHECK)
|
||||||
@echo "Updating unparam"
|
@echo "Updating unparam"
|
||||||
go get -u -v $(UNPARAM)
|
go get -u -v $(UNPARAM)
|
||||||
|
@echo "Updating statik"
|
||||||
|
go get -u -v $(STATIK)
|
||||||
|
|
||||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||||
# unless there is a reason not to.
|
# unless there is a reason not to.
|
||||||
|
|
|
@ -6,5 +6,5 @@
|
||||||
"Deadline": "500s",
|
"Deadline": "500s",
|
||||||
"Vendor": true,
|
"Vendor": true,
|
||||||
"Cyclo": 11,
|
"Cyclo": 11,
|
||||||
"Exclude": ["/usr/lib/go/src/"]
|
"Exclude": ["/usr/lib/go/src/", "client/lcd/statik/statik.go"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,12 @@ func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecod
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := codec.MarshalJSONIndent(cdc, acc)
|
var output []byte
|
||||||
|
if cliCtx.Indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(acc, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(acc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,12 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
json, err := cdc.MarshalJSON(newTx)
|
var json []byte
|
||||||
|
if cliCtx.Indent {
|
||||||
|
json, err = cdc.MarshalJSONIndent(newTx, "", " ")
|
||||||
|
} else {
|
||||||
|
json, err = cdc.MarshalJSON(newTx)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec,
|
||||||
"/accounts/{address}",
|
"/accounts/{address}",
|
||||||
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
||||||
).Methods("GET")
|
).Methods("GET")
|
||||||
|
r.HandleFunc(
|
||||||
|
"/bank/balances/{address}",
|
||||||
|
QueryBalancesRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
||||||
|
).Methods("GET")
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
"/tx/sign",
|
"/tx/sign",
|
||||||
SignTxRequestHandlerFn(cdc, cliCtx),
|
SignTxRequestHandlerFn(cdc, cliCtx),
|
||||||
|
@ -32,6 +36,7 @@ func QueryAccountRequestHandlerFn(
|
||||||
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
||||||
) http.HandlerFunc {
|
) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bech32addr := vars["address"]
|
bech32addr := vars["address"]
|
||||||
|
|
||||||
|
@ -60,13 +65,45 @@ func QueryAccountRequestHandlerFn(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// print out whole account
|
utils.PostProcessResponse(w, cdc, account, cliCtx.Indent)
|
||||||
output, err := cdc.MarshalJSON(account)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// query accountREST Handler
|
||||||
|
func QueryBalancesRequestHandlerFn(
|
||||||
|
storeName string, cdc *codec.Codec,
|
||||||
|
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
||||||
|
) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bech32addr := vars["address"]
|
||||||
|
|
||||||
|
addr, err := sdk.AccAddressFromBech32(bech32addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))
|
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(output)
|
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()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the query will return empty if there is no data for this account
|
||||||
|
if len(res) == 0 {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PostProcessResponse(w, cdc, account.GetCoins(), cliCtx.Indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,6 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := codec.MarshalJSONIndent(cdc, signedTx)
|
utils.PostProcessResponse(w, cdc, signedTx, cliCtx.Indent)
|
||||||
if err != nil {
|
|
||||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,13 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) {
|
||||||
store.Set(AddressStoreKey(addr), bz)
|
store.Set(AddressStoreKey(addr), bz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveAccount removes an account for the account mapper store.
|
||||||
|
func (am AccountMapper) RemoveAccount(ctx sdk.Context, acc Account) {
|
||||||
|
addr := acc.GetAddress()
|
||||||
|
store := ctx.KVStore(am.key)
|
||||||
|
store.Delete(AddressStoreKey(addr))
|
||||||
|
}
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
|
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
|
||||||
store := ctx.KVStore(am.key)
|
store := ctx.KVStore(am.key)
|
||||||
|
|
|
@ -61,6 +61,44 @@ func TestAccountMapperGetSet(t *testing.T) {
|
||||||
require.Equal(t, newSequence, acc.GetSequence())
|
require.Equal(t, newSequence, acc.GetSequence())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccountMapperRemoveAccount(t *testing.T) {
|
||||||
|
ms, capKey, _ := setupMultiStore()
|
||||||
|
cdc := codec.New()
|
||||||
|
RegisterBaseAccount(cdc)
|
||||||
|
|
||||||
|
// make context and mapper
|
||||||
|
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
|
||||||
|
mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount)
|
||||||
|
|
||||||
|
addr1 := sdk.AccAddress([]byte("addr1"))
|
||||||
|
addr2 := sdk.AccAddress([]byte("addr2"))
|
||||||
|
|
||||||
|
// create accounts
|
||||||
|
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
|
||||||
|
acc2 := mapper.NewAccountWithAddress(ctx, addr2)
|
||||||
|
|
||||||
|
accSeq1 := int64(20)
|
||||||
|
accSeq2 := int64(40)
|
||||||
|
|
||||||
|
acc1.SetSequence(accSeq1)
|
||||||
|
acc2.SetSequence(accSeq2)
|
||||||
|
mapper.SetAccount(ctx, acc1)
|
||||||
|
mapper.SetAccount(ctx, acc2)
|
||||||
|
|
||||||
|
acc1 = mapper.GetAccount(ctx, addr1)
|
||||||
|
require.NotNil(t, acc1)
|
||||||
|
require.Equal(t, accSeq1, acc1.GetSequence())
|
||||||
|
|
||||||
|
// remove one account
|
||||||
|
mapper.RemoveAccount(ctx, acc1)
|
||||||
|
acc1 = mapper.GetAccount(ctx, addr1)
|
||||||
|
require.Nil(t, acc1)
|
||||||
|
|
||||||
|
acc2 = mapper.GetAccount(ctx, addr2)
|
||||||
|
require.NotNil(t, acc2)
|
||||||
|
require.Equal(t, accSeq2, acc2.GetSequence())
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
|
func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
|
||||||
ms, capKey, _ := setupMultiStore()
|
ms, capKey, _ := setupMultiStore()
|
||||||
cdc := codec.New()
|
cdc := codec.New()
|
||||||
|
|
|
@ -33,12 +33,7 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := codec.MarshalJSONIndent(cdc, res)
|
utils.PostProcessResponse(w, cdc, res, cliCtx.Indent)
|
||||||
if err != nil {
|
|
||||||
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
|
|
||||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) {
|
||||||
r.HandleFunc("/accounts/{address}/send", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST")
|
r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST")
|
||||||
r.HandleFunc("/tx/broadcast", BroadcastTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
|
r.HandleFunc("/tx/broadcast", BroadcastTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ func TestTallyNoOneVotes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -69,6 +70,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -98,6 +100,7 @@ func TestTallyOnlyValidators51No(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -126,6 +129,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -157,6 +161,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -188,6 +193,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -219,6 +225,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -250,6 +257,7 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
|
@ -279,6 +287,7 @@ func TestTallyDelgatorOverride(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
|
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
|
||||||
stakeHandler(ctx, delegator1Msg)
|
stakeHandler(ctx, delegator1Msg)
|
||||||
|
@ -315,6 +324,7 @@ func TestTallyDelgatorInherit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
|
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
|
||||||
stakeHandler(ctx, delegator1Msg)
|
stakeHandler(ctx, delegator1Msg)
|
||||||
|
@ -349,6 +359,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
|
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
|
||||||
stakeHandler(ctx, delegator1Msg)
|
stakeHandler(ctx, delegator1Msg)
|
||||||
|
@ -402,6 +413,8 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
|
||||||
delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10))
|
delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10))
|
||||||
stakeHandler(ctx, delegator1Msg2)
|
stakeHandler(ctx, delegator1Msg2)
|
||||||
|
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
proposal.SetStatus(StatusVotingPeriod)
|
proposal.SetStatus(StatusVotingPeriod)
|
||||||
|
@ -432,6 +445,7 @@ func TestTallyJailedValidator(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7})
|
createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7})
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
|
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
|
||||||
stakeHandler(ctx, delegator1Msg)
|
stakeHandler(ctx, delegator1Msg)
|
||||||
|
@ -443,6 +457,8 @@ func TestTallyJailedValidator(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
|
sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
|
||||||
|
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
|
||||||
proposalID := proposal.GetProposalID()
|
proposalID := proposal.GetProposalID()
|
||||||
proposal.SetStatus(StatusVotingPeriod)
|
proposal.SetStatus(StatusVotingPeriod)
|
||||||
|
|
|
@ -36,12 +36,8 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap
|
||||||
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)})
|
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)})
|
||||||
validators = make(map[string]mockValidator)
|
validators = make(map[string]mockValidator)
|
||||||
for _, validator := range res.Validators {
|
for _, validator := range res.Validators {
|
||||||
pubkey, err := tmtypes.PB2TM.PubKey(validator.PubKey)
|
str := fmt.Sprintf("%v", validator.PubKey)
|
||||||
if err != nil {
|
validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
address := pubkey.Address()
|
|
||||||
validators[string(address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(setups); i++ {
|
for i := 0; i < len(setups); i++ {
|
||||||
|
@ -400,22 +396,23 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
|
||||||
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {
|
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {
|
||||||
|
|
||||||
for _, update := range updates {
|
for _, update := range updates {
|
||||||
|
str := fmt.Sprintf("%v", update.PubKey)
|
||||||
switch {
|
switch {
|
||||||
case update.Power == 0:
|
case update.Power == 0:
|
||||||
if _, ok := current[string(update.PubKey.Data)]; !ok {
|
if _, ok := current[str]; !ok {
|
||||||
tb.Fatalf("tried to delete a nonexistent validator")
|
tb.Fatalf("tried to delete a nonexistent validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
event("endblock/validatorupdates/kicked")
|
event("endblock/validatorupdates/kicked")
|
||||||
delete(current, string(update.PubKey.Data))
|
delete(current, str)
|
||||||
default:
|
default:
|
||||||
// Does validator already exist?
|
// Does validator already exist?
|
||||||
if mVal, ok := current[string(update.PubKey.Data)]; ok {
|
if mVal, ok := current[str]; ok {
|
||||||
mVal.val = update
|
mVal.val = update
|
||||||
event("endblock/validatorupdates/updated")
|
event("endblock/validatorupdates/updated")
|
||||||
} else {
|
} else {
|
||||||
// Set this new validator
|
// Set this new validator
|
||||||
current[string(update.PubKey.Data)] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
|
current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
|
||||||
event("endblock/validatorupdates/added")
|
event("endblock/validatorupdates/added")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,9 @@ func TestJailedValidatorDelegations(t *testing.T) {
|
||||||
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
|
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
|
||||||
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
|
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, stakeKeeper)
|
||||||
|
|
||||||
// set dummy signing info
|
// set dummy signing info
|
||||||
newInfo := ValidatorSigningInfo{
|
newInfo := ValidatorSigningInfo{
|
||||||
StartHeight: int64(0),
|
StartHeight: int64(0),
|
||||||
|
|
|
@ -85,13 +85,16 @@ func TestSlashingPeriodCap(t *testing.T) {
|
||||||
|
|
||||||
// double sign less than max age
|
// double sign less than max age
|
||||||
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
||||||
|
|
||||||
// should be jailed
|
// should be jailed
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
// update block height
|
// update block height
|
||||||
ctx = ctx.WithBlockHeight(int64(1))
|
ctx = ctx.WithBlockHeight(int64(1))
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, valConsAddr)
|
sk.Unjail(ctx, valConsAddr)
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
// power should be reduced
|
// power should be reduced
|
||||||
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
||||||
|
@ -100,10 +103,14 @@ func TestSlashingPeriodCap(t *testing.T) {
|
||||||
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
|
||||||
// should be jailed
|
// should be jailed
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
// update block height
|
// update block height
|
||||||
ctx = ctx.WithBlockHeight(int64(2))
|
ctx = ctx.WithBlockHeight(int64(2))
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, valConsAddr)
|
sk.Unjail(ctx, valConsAddr)
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
// power should be equal, no more should have been slashed
|
// power should be equal, no more should have been slashed
|
||||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
||||||
|
@ -114,6 +121,8 @@ func TestSlashingPeriodCap(t *testing.T) {
|
||||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||||
// unjail to measure power
|
// unjail to measure power
|
||||||
sk.Unjail(ctx, valConsAddr)
|
sk.Unjail(ctx, valConsAddr)
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
// power should be reduced
|
// power should be reduced
|
||||||
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
|
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
|
||||||
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
|
||||||
|
@ -180,6 +189,9 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
// validator should have been jailed
|
// validator should have been jailed
|
||||||
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||||
|
@ -193,6 +205,9 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
got = slh(ctx, NewMsgUnjail(addr))
|
got = slh(ctx, NewMsgUnjail(addr))
|
||||||
require.True(t, got.IsOK())
|
require.True(t, got.IsOK())
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
// validator should be rebonded now
|
// validator should be rebonded now
|
||||||
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
|
@ -222,12 +237,19 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
// validator should be jailed again after 500 unsigned blocks
|
// validator should be jailed again after 500 unsigned blocks
|
||||||
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
|
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
|
||||||
for ; height <= nextHeight; height++ {
|
for ; height <= nextHeight; height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||||
}
|
}
|
||||||
|
@ -296,6 +318,9 @@ func TestHandleAlreadyJailed(t *testing.T) {
|
||||||
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
// validator should have been jailed and slashed
|
// validator should have been jailed and slashed
|
||||||
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
require.Equal(t, sdk.Unbonding, validator.GetStatus())
|
||||||
|
|
|
@ -77,6 +77,9 @@ func TestBeginBlocker(t *testing.T) {
|
||||||
BeginBlocker(ctx, req, keeper)
|
BeginBlocker(ctx, req, keeper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
|
||||||
// validator should be jailed
|
// validator should be jailed
|
||||||
validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk))
|
validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
// Returns final validator set after applying all declaration and delegations
|
// Returns final validator set after applying all declaration and delegations
|
||||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
|
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
|
||||||
keeper.SetPool(ctx, data.Pool)
|
keeper.SetPool(ctx, data.Pool)
|
||||||
keeper.SetNewParams(ctx, data.Params)
|
keeper.SetParams(ctx, data.Params)
|
||||||
keeper.InitIntraTxCounter(ctx)
|
keeper.InitIntraTxCounter(ctx)
|
||||||
|
|
||||||
for i, validator := range data.Validators {
|
for i, validator := range data.Validators {
|
||||||
|
@ -31,26 +31,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
|
||||||
return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator)
|
return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually set indexes for the first time
|
// Manually set indices for the first time
|
||||||
keeper.SetValidatorByConsAddr(ctx, validator)
|
keeper.SetValidatorByConsAddr(ctx, validator)
|
||||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||||
|
|
||||||
if validator.Status == sdk.Bonded {
|
|
||||||
keeper.SetValidatorBondedIndex(ctx, validator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bond := range data.Bonds {
|
for _, bond := range data.Bonds {
|
||||||
keeper.SetDelegation(ctx, bond)
|
keeper.SetDelegation(ctx, bond)
|
||||||
}
|
}
|
||||||
|
|
||||||
keeper.UpdateBondedValidatorsFull(ctx)
|
res = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
vals := keeper.GetValidatorsBonded(ctx)
|
|
||||||
res = make([]abci.ValidatorUpdate, len(vals))
|
|
||||||
for i, val := range vals {
|
|
||||||
res[i] = val.ABCIValidatorUpdate()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
|
||||||
k.SetIntraTxCounter(ctx, 0)
|
k.SetIntraTxCounter(ctx, 0)
|
||||||
|
|
||||||
// calculate validator set changes
|
// calculate validator set changes
|
||||||
ValidatorUpdates = k.GetTendermintUpdates(ctx)
|
ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
||||||
msg.Commission.Rate, msg.Commission.MaxChangeRate,
|
msg.Commission.Rate, msg.Commission.MaxChangeRate,
|
||||||
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
|
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
|
||||||
)
|
)
|
||||||
|
|
||||||
validator, err := validator.SetInitialCommission(commission)
|
validator, err := validator.SetInitialCommission(commission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Result()
|
return err.Result()
|
||||||
|
@ -90,6 +89,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
||||||
|
|
||||||
k.SetValidator(ctx, validator)
|
k.SetValidator(ctx, validator)
|
||||||
k.SetValidatorByConsAddr(ctx, validator)
|
k.SetValidatorByConsAddr(ctx, validator)
|
||||||
|
k.SetNewValidatorByPowerIndex(ctx, validator)
|
||||||
|
|
||||||
// move coins from the msg.Address account to a (self-delegation) delegator account
|
// move coins from the msg.Address account to a (self-delegation) delegator account
|
||||||
// the validator account and global shares are updated within here
|
// the validator account and global shares are updated within here
|
||||||
|
@ -130,13 +130,13 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
|
||||||
validator.Description = description
|
validator.Description = description
|
||||||
|
|
||||||
if msg.CommissionRate != nil {
|
if msg.CommissionRate != nil {
|
||||||
if err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate); err != nil {
|
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
|
||||||
|
if err != nil {
|
||||||
return err.Result()
|
return err.Result()
|
||||||
}
|
}
|
||||||
|
validator.Commission = commission
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't need to run through all the power update logic within k.UpdateValidator
|
|
||||||
// We just need to override the entry in state, since only the description has changed.
|
|
||||||
k.SetValidator(ctx, validator)
|
k.SetValidator(ctx, validator)
|
||||||
|
|
||||||
tags := sdk.NewTags(
|
tags := sdk.NewTags(
|
||||||
|
|
|
@ -63,6 +63,10 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||||
|
|
||||||
|
// must end-block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
// verify the self-delegation exists
|
// verify the self-delegation exists
|
||||||
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
|
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -83,14 +87,20 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||||
|
|
||||||
|
// must end-block
|
||||||
|
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
// slash and jail the first validator
|
// slash and jail the first validator
|
||||||
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
|
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
|
||||||
keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1))
|
keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1))
|
||||||
keeper.Jail(ctx, consAddr0)
|
keeper.Jail(ctx, consAddr0)
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
|
require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
|
||||||
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
|
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
|
||||||
|
keeper.Unjail(ctx, consAddr0)
|
||||||
|
|
||||||
// the old power record should have been deleted as the power changed
|
// the old power record should have been deleted as the power changed
|
||||||
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||||
|
@ -121,6 +131,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
||||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||||
|
|
||||||
|
EndBlocker(ctx, keeper)
|
||||||
|
|
||||||
// verify that by power key nolonger exists
|
// verify that by power key nolonger exists
|
||||||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.False(t, found)
|
require.False(t, found)
|
||||||
|
@ -136,8 +148,10 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||||
msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10)
|
msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10)
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper)
|
||||||
require.True(t, got.IsOK(), "%v", got)
|
require.True(t, got.IsOK(), "%v", got)
|
||||||
validator, found := keeper.GetValidator(ctx, addr1)
|
|
||||||
|
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
|
validator, found := keeper.GetValidator(ctx, addr1)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.Equal(t, sdk.Bonded, validator.Status)
|
assert.Equal(t, sdk.Bonded, validator.Status)
|
||||||
assert.Equal(t, addr1, validator.OperatorAddr)
|
assert.Equal(t, addr1, validator.OperatorAddr)
|
||||||
|
@ -160,6 +174,11 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||||
msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10)
|
msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10)
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper)
|
got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper)
|
||||||
require.True(t, got.IsOK(), "%v", got)
|
require.True(t, got.IsOK(), "%v", got)
|
||||||
|
|
||||||
|
// must end-block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found = keeper.GetValidator(ctx, addr2)
|
validator, found = keeper.GetValidator(ctx, addr2)
|
||||||
|
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -180,6 +199,11 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
|
||||||
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
|
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
|
||||||
require.True(t, got.IsOK(), "%v", got)
|
require.True(t, got.IsOK(), "%v", got)
|
||||||
|
|
||||||
|
// must end-block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
|
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -211,6 +235,10 @@ func TestLegacyValidatorDelegations(t *testing.T) {
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
|
||||||
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
||||||
|
|
||||||
|
// must end-block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
// verify the validator exists and has the correct attributes
|
// verify the validator exists and has the correct attributes
|
||||||
validator, found := keeper.GetValidator(ctx, valAddr)
|
validator, found := keeper.GetValidator(ctx, valAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -302,6 +330,9 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
|
||||||
|
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status)
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
|
@ -384,6 +415,9 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
||||||
amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
|
amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
|
||||||
require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted")
|
require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted")
|
||||||
|
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
|
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
|
||||||
|
@ -533,11 +567,8 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
||||||
// unbond them all
|
// unbond them all
|
||||||
for i, delegatorAddr := range delegatorAddrs {
|
for i, delegatorAddr := range delegatorAddrs {
|
||||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
|
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
|
||||||
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
|
|
||||||
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
|
||||||
|
|
||||||
//Check that the account is unbonded
|
//Check that the account is unbonded
|
||||||
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||||
|
@ -564,11 +595,8 @@ func TestJailValidator(t *testing.T) {
|
||||||
|
|
||||||
// unbond the validators bond portion
|
// unbond the validators bond portion
|
||||||
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
|
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
|
||||||
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(sdk.AccAddress(validatorAddr), validatorAddr)
|
|
||||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
|
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error")
|
require.True(t, got.IsOK(), "expected no error: %v", got)
|
||||||
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error")
|
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -750,16 +778,22 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
|
||||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
|
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx)))
|
require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx)))
|
||||||
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
|
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
|
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
|
||||||
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
|
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
|
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
|
||||||
|
|
||||||
// unbond the valdator-2
|
// unbond the valdator-2
|
||||||
|
@ -767,6 +801,9 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
|
||||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
|
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
|
||||||
|
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
// because there are extra validators waiting to get in, the queued
|
// because there are extra validators waiting to get in, the queued
|
||||||
// validator (aka. validator-1) should make it into the bonded group, thus
|
// validator (aka. validator-1) should make it into the bonded group, thus
|
||||||
// the total number of validators should stay the same
|
// the total number of validators should stay the same
|
||||||
|
@ -777,142 +814,6 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
|
||||||
require.Equal(t, sdk.Bonded, val1.Status, "%v", val1)
|
require.Equal(t, sdk.Bonded, val1.Status, "%v", val1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJoiningAsCliffValidator(t *testing.T) {
|
|
||||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
|
||||||
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
|
|
||||||
|
|
||||||
// make sure that the cliff validator is nil to begin with
|
|
||||||
cliffVal := keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// set the unbonding time
|
|
||||||
params := keeper.GetParams(ctx)
|
|
||||||
params.UnbondingTime = 0
|
|
||||||
params.MaxValidators = 2
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
|
|
||||||
// add the first validator
|
|
||||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
|
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// cliff validator should still be nil
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// Add the second validator
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
|
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// now that we've reached maximum validators, the val-2 should be added to the cliff (top)
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJoiningToCreateFirstCliffValidator(t *testing.T) {
|
|
||||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
|
||||||
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
|
|
||||||
|
|
||||||
// make sure that the cliff validator is nil to begin with
|
|
||||||
cliffVal := keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// set the unbonding time
|
|
||||||
params := keeper.GetParams(ctx)
|
|
||||||
params.UnbondingTime = 0
|
|
||||||
params.MaxValidators = 2
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
|
|
||||||
// add the first validator
|
|
||||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
|
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// cliff validator should still be nil
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// Add the second validator
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 60)
|
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// now that we've reached maximum validators, validator-1 should be added to the cliff (top)
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, validatorAddr1.Bytes(), cliffVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCliffValidator(t *testing.T) {
|
|
||||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
|
||||||
validatorAddr1 := sdk.ValAddress(keep.Addrs[0])
|
|
||||||
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
|
|
||||||
validatorAddr3 := sdk.ValAddress(keep.Addrs[2])
|
|
||||||
|
|
||||||
// make sure that the cliff validator is nil to begin with
|
|
||||||
cliffVal := keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// set the unbonding time
|
|
||||||
params := keeper.GetParams(ctx)
|
|
||||||
params.UnbondingTime = 0
|
|
||||||
params.MaxValidators = 2
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
|
|
||||||
// add the first validator
|
|
||||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
|
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// cliff validator should still be nil
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
|
|
||||||
// Add the second validator
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
|
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
// now that we've reached maximum validators, validator-2 should be added to the cliff (top)
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
|
|
||||||
|
|
||||||
// add the third validator, which should not make it to being bonded,
|
|
||||||
// so the cliff validator should not change because nobody has been kicked out
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
|
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
|
||||||
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
|
|
||||||
|
|
||||||
// unbond valdator-2
|
|
||||||
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30))
|
|
||||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
|
|
||||||
|
|
||||||
vals := keeper.GetValidatorsBonded(ctx)
|
|
||||||
require.Equal(t, 2, len(vals))
|
|
||||||
|
|
||||||
// now the validator set should be updated,
|
|
||||||
// where val-3 enters the validator set on the cliff
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, validatorAddr3.Bytes(), cliffVal)
|
|
||||||
|
|
||||||
// unbond valdator-1
|
|
||||||
msgBeginUnbonding = NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr1), validatorAddr1, sdk.NewDec(50))
|
|
||||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
|
|
||||||
|
|
||||||
// get bonded validators - should just be one
|
|
||||||
vals = keeper.GetValidatorsBonded(ctx)
|
|
||||||
require.Equal(t, 1, len(vals))
|
|
||||||
|
|
||||||
// cliff now should be empty
|
|
||||||
cliffVal = keeper.GetCliffValidator(ctx)
|
|
||||||
require.Equal(t, []byte(nil), cliffVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||||
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
|
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
|
||||||
|
@ -931,6 +832,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgDelegate")
|
require.True(t, got.IsOK(), "expected no error on runMsgDelegate")
|
||||||
|
|
||||||
|
// apply Tendermint updates
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 2, len(updates))
|
||||||
|
|
||||||
// a block passes
|
// a block passes
|
||||||
ctx = ctx.WithBlockHeight(1)
|
ctx = ctx.WithBlockHeight(1)
|
||||||
|
|
||||||
|
@ -949,6 +854,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.NewDec(6), delegation.Shares)
|
require.Equal(t, sdk.NewDec(6), delegation.Shares)
|
||||||
|
|
||||||
|
// must apply validator updates
|
||||||
|
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 2, len(updates))
|
||||||
|
|
||||||
// slash the validator by half
|
// slash the validator by half
|
||||||
keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1))
|
keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1))
|
||||||
|
|
||||||
|
@ -991,6 +900,9 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.NewDec(3), delegation.Shares)
|
require.Equal(t, sdk.NewDec(3), delegation.Shares)
|
||||||
|
|
||||||
|
// end blocker
|
||||||
|
EndBlocker(ctx, keeper)
|
||||||
|
|
||||||
// validator power should have been reduced to zero
|
// validator power should have been reduced to zero
|
||||||
// ergo validator should have been removed from the store
|
// ergo validator should have been removed from the store
|
||||||
_, found = keeper.GetValidator(ctx, valA)
|
_, found = keeper.GetValidator(ctx, valA)
|
||||||
|
|
|
@ -41,9 +41,3 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
|
||||||
|
|
||||||
The transient store persists between transations but not between blocks
|
The transient store persists between transations but not between blocks
|
||||||
|
|
||||||
## Tendermint Updates
|
|
||||||
- Prefix Key Space: TendermintUpdatesTKey
|
|
||||||
- Key/Sort: Validator Operator Address
|
|
||||||
- Value: Tendermint ABCI Validator
|
|
||||||
- Contains: Validators are queued to affect the consensus validation set in Tendermint
|
|
||||||
- Used For: Informing Tendermint of the validator set updates
|
|
||||||
|
|
|
@ -265,18 +265,13 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pool := k.GetPool(ctx)
|
validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt.Amount)
|
||||||
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount)
|
|
||||||
|
// Update delegation
|
||||||
delegation.Shares = delegation.Shares.Add(newShares)
|
delegation.Shares = delegation.Shares.Add(newShares)
|
||||||
|
|
||||||
// Update delegation height
|
|
||||||
delegation.Height = ctx.BlockHeight()
|
delegation.Height = ctx.BlockHeight()
|
||||||
|
|
||||||
k.SetPool(ctx, pool)
|
|
||||||
k.SetDelegation(ctx, delegation)
|
k.SetDelegation(ctx, delegation)
|
||||||
k.UpdateValidator(ctx, validator)
|
return newShares, nil
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unbond the the delegation return
|
// unbond the the delegation return
|
||||||
|
@ -313,8 +308,9 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
||||||
|
|
||||||
// if the delegation is the operator of the validator then
|
// if the delegation is the operator of the validator then
|
||||||
// trigger a jail validator
|
// trigger a jail validator
|
||||||
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && validator.Jailed == false {
|
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && !validator.Jailed {
|
||||||
validator.Jailed = true
|
k.jailValidator(ctx, validator)
|
||||||
|
validator = k.mustGetValidator(ctx, validator.OperatorAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
k.RemoveDelegation(ctx, delegation)
|
k.RemoveDelegation(ctx, delegation)
|
||||||
|
@ -325,14 +321,10 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the coins from the validator
|
// remove the coins from the validator
|
||||||
pool := k.GetPool(ctx)
|
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
|
||||||
validator, pool, amount = validator.RemoveDelShares(pool, shares)
|
|
||||||
|
|
||||||
k.SetPool(ctx, pool)
|
if validator.DelegatorShares.IsZero() && validator.Status != sdk.Bonded {
|
||||||
|
// if bonded, we must remove in EndBlocker instead
|
||||||
// update then remove validator if necessary
|
|
||||||
validator = k.UpdateValidator(ctx, validator)
|
|
||||||
if validator.DelegatorShares.IsZero() {
|
|
||||||
k.RemoveValidator(ctx, validator.OperatorAddr)
|
k.RemoveValidator(ctx, validator.OperatorAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +360,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete unbonding an unbonding record
|
// begin unbonding an unbonding record
|
||||||
func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
||||||
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
|
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@ func TestDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
|
||||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
|
||||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
|
||||||
|
|
||||||
// first add a validators[0] to delegate too
|
// first add a validators[0] to delegate too
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ func TestUnbondDelegation(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
|
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
|
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
|
||||||
|
@ -226,7 +226,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
||||||
|
@ -236,10 +236,11 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, selfDelegation)
|
keeper.SetDelegation(ctx, selfDelegation)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
|
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -252,6 +253,10 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
||||||
|
@ -269,7 +274,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
|
||||||
|
@ -279,10 +284,11 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, selfDelegation)
|
keeper.SetDelegation(ctx, selfDelegation)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
|
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -303,6 +309,10 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||||
|
@ -340,7 +350,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
|
@ -351,10 +361,11 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, selfDelegation)
|
keeper.SetDelegation(ctx, selfDelegation)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
|
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -374,6 +385,10 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
|
||||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||||
|
@ -506,7 +521,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
|
@ -520,14 +535,16 @@ func TestRedelegateSelfDelegation(t *testing.T) {
|
||||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
|
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(10))
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
validator2 = testingUpdateValidator(keeper, ctx, validator2)
|
||||||
|
require.Equal(t, sdk.Bonded, validator2.Status)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -539,6 +556,10 @@ func TestRedelegateSelfDelegation(t *testing.T) {
|
||||||
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
|
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 2, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
|
||||||
|
@ -552,11 +573,12 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||||
|
|
||||||
//create a validator with a self-delegation
|
//create a validator with a self-delegation
|
||||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||||
|
validator.BondIntraTxCounter = 1
|
||||||
|
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
|
@ -567,10 +589,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, selfDelegation)
|
keeper.SetDelegation(ctx, selfDelegation)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
|
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -581,10 +604,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||||
|
|
||||||
// create a second validator
|
// create a second validator
|
||||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||||
|
validator2.BondIntraTxCounter = 2
|
||||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
validator2 = testingUpdateValidator(keeper, ctx, validator2)
|
||||||
|
|
||||||
header := ctx.BlockHeader()
|
header := ctx.BlockHeader()
|
||||||
blockHeight := int64(10)
|
blockHeight := int64(10)
|
||||||
|
@ -597,6 +621,10 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||||
|
@ -634,7 +662,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||||
selfDelegation := types.Delegation{
|
selfDelegation := types.Delegation{
|
||||||
|
@ -645,10 +673,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, selfDelegation)
|
keeper.SetDelegation(ctx, selfDelegation)
|
||||||
|
|
||||||
// create a second delegation to this validator
|
// create a second delegation to this validator
|
||||||
|
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
|
||||||
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
|
validator.BondIntraTxCounter = 1
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -659,10 +689,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||||
|
|
||||||
// create a second validator
|
// create a second validator
|
||||||
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
|
||||||
|
validator2.BondIntraTxCounter = 2
|
||||||
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
|
||||||
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
require.Equal(t, int64(10), issuedShares.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator2 = keeper.UpdateValidator(ctx, validator2)
|
validator2 = testingUpdateValidator(keeper, ctx, validator2)
|
||||||
|
require.Equal(t, sdk.Bonded, validator2.Status)
|
||||||
|
|
||||||
header := ctx.BlockHeader()
|
header := ctx.BlockHeader()
|
||||||
blockHeight := int64(10)
|
blockHeight := int64(10)
|
||||||
|
@ -675,6 +707,10 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||||
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
require.Equal(t, blockHeight, validator.UnbondingHeight)
|
||||||
|
|
|
@ -64,25 +64,9 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need a distinct function because setParams depends on an existing previous
|
|
||||||
// record of params to exist (to check if maxValidators has changed) - and we
|
|
||||||
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
|
|
||||||
// first params set it will panic.
|
|
||||||
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
|
|
||||||
store := ctx.KVStore(k.storeKey)
|
|
||||||
b := k.cdc.MustMarshalBinary(params)
|
|
||||||
store.Set(ParamKey, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the params
|
// set the params
|
||||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
exParams := k.GetParams(ctx)
|
|
||||||
|
|
||||||
// if max validator count changes, must recalculate validator set
|
|
||||||
if exParams.MaxValidators != params.MaxValidators {
|
|
||||||
k.UpdateBondedValidatorsFull(ctx)
|
|
||||||
}
|
|
||||||
b := k.cdc.MustMarshalBinary(params)
|
b := k.cdc.MustMarshalBinary(params)
|
||||||
store.Set(ParamKey, b)
|
store.Set(ParamKey, b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,13 @@ var (
|
||||||
ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
|
ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
|
||||||
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
|
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
|
||||||
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
|
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
|
||||||
ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator
|
IntraTxCounterKey = []byte{0x06} // key for intra-block tx index
|
||||||
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
|
DelegationKey = []byte{0x07} // key for a delegation
|
||||||
IntraTxCounterKey = []byte{0x08} // key for intra-block tx index
|
UnbondingDelegationKey = []byte{0x08} // key for an unbonding-delegation
|
||||||
DelegationKey = []byte{0x09} // key for a delegation
|
UnbondingDelegationByValIndexKey = []byte{0x09} // prefix for each key for an unbonding-delegation, by validator operator
|
||||||
UnbondingDelegationKey = []byte{0x0A} // key for an unbonding-delegation
|
RedelegationKey = []byte{0x0A} // key for a redelegation
|
||||||
UnbondingDelegationByValIndexKey = []byte{0x0B} // prefix for each key for an unbonding-delegation, by validator operator
|
RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator
|
||||||
RedelegationKey = []byte{0x0C} // key for a redelegation
|
RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator
|
||||||
RedelegationByValSrcIndexKey = []byte{0x0D} // prefix for each key for an redelegation, by source validator operator
|
|
||||||
RedelegationByValDstIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by destination validator operator
|
|
||||||
|
|
||||||
// Keys for store prefixes (transient)
|
|
||||||
TendermintUpdatesTKey = []byte{0x00} // prefix for each key to a validator which is being updated
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||||
|
@ -46,12 +41,6 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte {
|
||||||
return append(ValidatorsByConsAddrKey, addr.Bytes()...)
|
return append(ValidatorsByConsAddrKey, addr.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the key for the current validator group
|
|
||||||
// VALUE: none (key rearrangement with GetValKeyFromValBondedIndexKey)
|
|
||||||
func GetValidatorsBondedIndexKey(operatorAddr sdk.ValAddress) []byte {
|
|
||||||
return append(ValidatorsBondedIndexKey, operatorAddr.Bytes()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the validator operator address from ValBondedIndexKey
|
// Get the validator operator address from ValBondedIndexKey
|
||||||
func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte {
|
func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte {
|
||||||
return IndexKey[1:] // remove prefix bytes
|
return IndexKey[1:] // remove prefix bytes
|
||||||
|
@ -63,43 +52,34 @@ func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte {
|
||||||
// VALUE: validator operator address ([]byte)
|
// VALUE: validator operator address ([]byte)
|
||||||
func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte {
|
func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte {
|
||||||
// NOTE the address doesn't need to be stored because counter bytes must always be different
|
// NOTE the address doesn't need to be stored because counter bytes must always be different
|
||||||
return getValidatorPowerRank(validator, pool)
|
return getValidatorPowerRank(validator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bonded validator index key for an operator address
|
||||||
|
func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorsBondedIndexKey, operator...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the power ranking of a validator
|
// get the power ranking of a validator
|
||||||
// NOTE the larger values are of higher value
|
// NOTE the larger values are of higher value
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
|
func getValidatorPowerRank(validator types.Validator) []byte {
|
||||||
|
|
||||||
potentialPower := validator.Tokens
|
potentialPower := validator.Tokens
|
||||||
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||||
|
powerBytesLen := len(powerBytes)
|
||||||
|
// key is of format prefix || powerbytes || heightBytes || counterBytes
|
||||||
|
key := make([]byte, 1+powerBytesLen+8+2)
|
||||||
|
|
||||||
jailedBytes := make([]byte, 1)
|
key[0] = ValidatorsByPowerIndexKey[0]
|
||||||
if validator.Jailed {
|
copy(key[1:powerBytesLen+1], powerBytes)
|
||||||
jailedBytes[0] = byte(0x00)
|
|
||||||
} else {
|
|
||||||
jailedBytes[0] = byte(0x01)
|
|
||||||
}
|
|
||||||
|
|
||||||
// heightBytes and counterBytes represent strings like powerBytes does
|
// include heightBytes height is inverted (older validators first)
|
||||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
binary.BigEndian.PutUint64(key[powerBytesLen+1:powerBytesLen+9], ^uint64(validator.BondHeight))
|
||||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
// include counterBytes, counter is inverted (first txns have priority)
|
||||||
counterBytes := make([]byte, 2)
|
binary.BigEndian.PutUint16(key[powerBytesLen+9:powerBytesLen+11], ^uint16(validator.BondIntraTxCounter))
|
||||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
|
||||||
|
|
||||||
return append(append(append(append(
|
return key
|
||||||
ValidatorsByPowerIndexKey,
|
|
||||||
jailedBytes...),
|
|
||||||
powerBytes...),
|
|
||||||
heightBytes...),
|
|
||||||
counterBytes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the key for the accumulated update validators
|
|
||||||
// VALUE: abci.Validator
|
|
||||||
// note records using these keys should never persist between blocks
|
|
||||||
func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte {
|
|
||||||
return append(TendermintUpdatesTKey, operatorAddr.Bytes()...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//______________________________________________________________________________
|
//______________________________________________________________________________
|
||||||
|
@ -159,28 +139,42 @@ func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte {
|
||||||
// gets the key for a redelegation
|
// gets the key for a redelegation
|
||||||
// VALUE: stake/types.RedelegationKey
|
// VALUE: stake/types.RedelegationKey
|
||||||
func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
return append(append(
|
key := make([]byte, 1+sdk.AddrLen*3)
|
||||||
GetREDsKey(delAddr.Bytes()),
|
|
||||||
valSrcAddr.Bytes()...),
|
copy(key[0:sdk.AddrLen+1], GetREDsKey(delAddr.Bytes()))
|
||||||
valDstAddr.Bytes()...)
|
copy(key[sdk.AddrLen+1:2*sdk.AddrLen+1], valSrcAddr.Bytes())
|
||||||
|
copy(key[2*sdk.AddrLen+1:3*sdk.AddrLen+1], valDstAddr.Bytes())
|
||||||
|
|
||||||
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the index-key for a redelegation, stored by source-validator-index
|
// gets the index-key for a redelegation, stored by source-validator-index
|
||||||
// VALUE: none (key rearrangement used)
|
// VALUE: none (key rearrangement used)
|
||||||
func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
return append(append(
|
REDSFromValsSrcKey := GetREDsFromValSrcIndexKey(valSrcAddr)
|
||||||
GetREDsFromValSrcIndexKey(valSrcAddr),
|
offset := len(REDSFromValsSrcKey)
|
||||||
delAddr.Bytes()...),
|
|
||||||
valDstAddr.Bytes()...)
|
// key is of the form REDSFromValsSrcKey || delAddr || valDstAddr
|
||||||
|
key := make([]byte, len(REDSFromValsSrcKey)+2*sdk.AddrLen)
|
||||||
|
copy(key[0:offset], REDSFromValsSrcKey)
|
||||||
|
copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes())
|
||||||
|
copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valDstAddr.Bytes())
|
||||||
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the index-key for a redelegation, stored by destination-validator-index
|
// gets the index-key for a redelegation, stored by destination-validator-index
|
||||||
// VALUE: none (key rearrangement used)
|
// VALUE: none (key rearrangement used)
|
||||||
func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
return append(append(
|
REDSToValsDstKey := GetREDsToValDstIndexKey(valDstAddr)
|
||||||
GetREDsToValDstIndexKey(valDstAddr),
|
offset := len(REDSToValsDstKey)
|
||||||
delAddr.Bytes()...),
|
|
||||||
valSrcAddr.Bytes()...)
|
// key is of the form REDSToValsDstKey || delAddr || valSrcAddr
|
||||||
|
key := make([]byte, len(REDSToValsDstKey)+2*sdk.AddrLen)
|
||||||
|
copy(key[0:offset], REDSToValsDstKey)
|
||||||
|
copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes())
|
||||||
|
copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valSrcAddr.Bytes())
|
||||||
|
|
||||||
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
// rearranges the ValSrcIndexKey to get the REDKey
|
// rearranges the ValSrcIndexKey to get the REDKey
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pk1 = ed25519.GenPrivKey().PubKey()
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetValidatorPowerRank(t *testing.T) {
|
||||||
|
valAddr1 := sdk.ValAddress(pk1.Bytes())
|
||||||
|
emptyDesc := types.Description{}
|
||||||
|
val1 := types.NewValidator(valAddr1, pk1, emptyDesc)
|
||||||
|
val1.Tokens = sdk.NewDec(12)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
validator types.Validator
|
||||||
|
wantHex string
|
||||||
|
}{
|
||||||
|
{val1, "05303030303030303030303132ffffffffffffffffffff"},
|
||||||
|
}
|
||||||
|
for i, tt := range tests {
|
||||||
|
got := hex.EncodeToString(getValidatorPowerRank(tt.validator))
|
||||||
|
|
||||||
|
assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i)
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,20 +95,18 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot decrease balance below zero
|
// cannot decrease balance below zero
|
||||||
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
|
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
|
||||||
|
|
||||||
// burn validator's tokens
|
// burn validator's tokens and update the validator
|
||||||
|
validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn)
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
|
|
||||||
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||||
k.SetPool(ctx, pool)
|
k.SetPool(ctx, pool)
|
||||||
|
|
||||||
// update the validator, possibly kicking it out
|
|
||||||
validator = k.UpdateValidator(ctx, validator)
|
|
||||||
|
|
||||||
// remove validator if it has no more tokens
|
// remove validator if it has no more tokens
|
||||||
if validator.Tokens.IsZero() {
|
if validator.Tokens.IsZero() && validator.Status != sdk.Bonded {
|
||||||
|
// if bonded, we must remove in ApplyAndReturnValidatorSetUpdates instead
|
||||||
k.RemoveValidator(ctx, validator.OperatorAddr)
|
k.RemoveValidator(ctx, validator.OperatorAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +121,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
|
||||||
|
|
||||||
// jail a validator
|
// jail a validator
|
||||||
func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
||||||
k.setJailed(ctx, consAddr, true)
|
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
|
||||||
|
k.jailValidator(ctx, validator)
|
||||||
logger := ctx.Logger().With("module", "x/stake")
|
logger := ctx.Logger().With("module", "x/stake")
|
||||||
logger.Info(fmt.Sprintf("validator %s jailed", consAddr))
|
logger.Info(fmt.Sprintf("validator %s jailed", consAddr))
|
||||||
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
||||||
|
@ -132,24 +131,14 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
||||||
|
|
||||||
// unjail a validator
|
// unjail a validator
|
||||||
func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
||||||
k.setJailed(ctx, consAddr, false)
|
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
|
||||||
|
k.unjailValidator(ctx, validator)
|
||||||
logger := ctx.Logger().With("module", "x/stake")
|
logger := ctx.Logger().With("module", "x/stake")
|
||||||
logger.Info(fmt.Sprintf("validator %s unjailed", consAddr))
|
logger.Info(fmt.Sprintf("validator %s unjailed", consAddr))
|
||||||
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the jailed flag on a validator
|
|
||||||
func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) {
|
|
||||||
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
|
|
||||||
if !found {
|
|
||||||
panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed))
|
|
||||||
}
|
|
||||||
validator.Jailed = isJailed
|
|
||||||
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// slash an unbonding delegation and update the pool
|
// slash an unbonding delegation and update the pool
|
||||||
// return the amount that would have been slashed assuming
|
// return the amount that would have been slashed assuming
|
||||||
// the unbonding delegation had enough stake to slash
|
// the unbonding delegation had enough stake to slash
|
||||||
|
|
|
@ -26,8 +26,10 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
|
||||||
for i := 0; i < numVals; i++ {
|
for i := 0; i < numVals; i++ {
|
||||||
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||||
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt))
|
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt))
|
||||||
|
validator.BondIntraTxCounter = int16(i)
|
||||||
|
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(amt))
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = testingUpdateValidator(keeper, ctx, validator)
|
||||||
keeper.SetValidatorByConsAddr(ctx, validator)
|
keeper.SetValidatorByConsAddr(ctx, validator)
|
||||||
}
|
}
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
|
@ -161,6 +163,10 @@ func TestSlashRedelegation(t *testing.T) {
|
||||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
// initialbalance unchanged
|
// initialbalance unchanged
|
||||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
|
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
|
||||||
|
|
||||||
|
@ -201,6 +207,11 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates)
|
||||||
|
|
||||||
|
validator = keeper.mustGetValidator(ctx, validator.OperatorAddr)
|
||||||
// power decreased
|
// power decreased
|
||||||
require.Equal(t, sdk.NewDec(5), validator.GetPower())
|
require.Equal(t, sdk.NewDec(5), validator.GetPower())
|
||||||
// pool bonded shares decreased
|
// pool bonded shares decreased
|
||||||
|
@ -232,6 +243,10 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
keeper.Slash(ctx, consAddr, 10, 10, fraction)
|
keeper.Slash(ctx, consAddr, 10, 10, fraction)
|
||||||
|
|
||||||
|
// end block
|
||||||
|
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
require.Equal(t, 1, len(updates))
|
||||||
|
|
||||||
// read updating unbonding delegation
|
// read updating unbonding delegation
|
||||||
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -301,6 +316,8 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// just 1 bonded token burned again since that's all the validator now has
|
// just 1 bonded token burned again since that's all the validator now has
|
||||||
require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
// read updated validator
|
// read updated validator
|
||||||
// power decreased by 1 again, validator is out of stake
|
// power decreased by 1 again, validator is out of stake
|
||||||
// ergo validator should have been removed from the store
|
// ergo validator should have been removed from the store
|
||||||
|
@ -402,6 +419,8 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// four more bonded tokens burned
|
// four more bonded tokens burned
|
||||||
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
|
// apply TM updates
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
// read updated validator
|
// read updated validator
|
||||||
// validator decreased to zero power, should have been removed from the store
|
// validator decreased to zero power, should have been removed from the store
|
||||||
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
|
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||||
|
|
|
@ -111,7 +111,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
|
||||||
ck := bank.NewBaseKeeper(accountMapper)
|
ck := bank.NewBaseKeeper(accountMapper)
|
||||||
keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace)
|
keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace)
|
||||||
keeper.SetPool(ctx, types.InitialPool())
|
keeper.SetPool(ctx, types.InitialPool())
|
||||||
keeper.SetNewParams(ctx, types.DefaultParams())
|
keeper.SetParams(ctx, types.DefaultParams())
|
||||||
keeper.InitIntraTxCounter(ctx)
|
keeper.InitIntraTxCounter(ctx)
|
||||||
|
|
||||||
// fill all the addresses with some coins, set the loose pool tokens simultaneously
|
// fill all the addresses with some coins, set the loose pool tokens simultaneously
|
||||||
|
@ -202,5 +202,22 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
|
||||||
// does a certain by-power index record exist
|
// does a certain by-power index record exist
|
||||||
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
|
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
|
||||||
store := ctx.KVStore(keeper.storeKey)
|
store := ctx.KVStore(keeper.storeKey)
|
||||||
return store.Get(power) != nil
|
return store.Has(power)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator {
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
keeper.SetValidator(ctx, validator)
|
||||||
|
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
|
||||||
|
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
validator, found := keeper.GetValidator(ctx, validator.OperatorAddr)
|
||||||
|
if !found {
|
||||||
|
panic("validator expected but not found")
|
||||||
|
}
|
||||||
|
return validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
return store.Has(power)
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue