Merge branch 'develop' into cwgoes/update-tendermint-upstream
This commit is contained in:
commit
e7c2a9614f
|
@ -154,6 +154,7 @@ jobs:
|
|||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make install
|
||||
export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')"
|
||||
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | grep -v '/simulation' | circleci tests split --split-by=timings); do
|
||||
id=$(basename "$pkg")
|
||||
GOCACHE=off go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||
|
|
|
@ -34,11 +34,11 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
|
||||
digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = "UT"
|
||||
revision = "cff30e1d23fc9e800b2b5b4b41ef1817dda07e9f"
|
||||
revision = "2a560b2036bee5e3679ec2133eb6520b2f195213"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2"
|
||||
|
@ -230,6 +230,14 @@
|
|||
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
|
@ -294,7 +302,7 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
|
||||
digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -303,7 +311,7 @@
|
|||
"xfs",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
|
||||
revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
|
||||
|
@ -423,12 +431,12 @@
|
|||
version = "v0.12.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e99ef92d64f2391efbbfb15310df635f96247532bbac2676ea43e466d706401d"
|
||||
digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430"
|
||||
name = "github.com/tendermint/iavl"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "e5726c0066ccdd299a2ec9262f93c7896cdfcd87"
|
||||
version = "v0.10.0"
|
||||
revision = "3acc91fb8811db2c5409a855ae1f8e441fe98e2d"
|
||||
version = "v0.11.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f4fcc1a4dbe079b200556ca26c1ff1dacf23712125b9c265d8f02c0dbc318f39"
|
||||
|
@ -499,14 +507,6 @@
|
|||
revision = "d419fffe18531317c28c29a292ad7d253f6cafdf"
|
||||
version = "v0.24.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e"
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
packages = ["cli"]
|
||||
pruneopts = "UT"
|
||||
revision = "49596e0a1f48866603813df843c9409fc19805c6"
|
||||
version = "v0.9.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666"
|
||||
name = "github.com/zondax/ledger-goclient"
|
||||
|
@ -536,7 +536,7 @@
|
|||
"salsa20/salsa",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "0709b304e793a5edb4a2c0145f281ecdc20838a4"
|
||||
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||
|
@ -556,14 +556,14 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e54041967955fe3ddc2f5d75060301f2c7ef1ee8fae9a76e9e238b8bff82eb12"
|
||||
digest = "1:68023dc297a659d5eb2dafd62eda811456b338c5b3ec3c27da79e8a47d3f456a"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "8cf3aee429924738c56c34bb819c4ea8273fc434"
|
||||
revision = "2f1df4e56cdeb503a08d8577e6f1a7eb12efab82"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
|
@ -590,11 +590,11 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
|
||||
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "11092d34479b07829b72e10713b159248caf5dad"
|
||||
revision = "0e822944c569bf5c9afd034adaa56208bd2906ac"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||
|
@ -648,6 +648,8 @@
|
|||
"github.com/golang/protobuf/proto",
|
||||
"github.com/gorilla/mux",
|
||||
"github.com/mattn/go-isatty",
|
||||
"github.com/mitchellh/go-homedir",
|
||||
"github.com/pelletier/go-toml",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/cobra",
|
||||
"github.com/spf13/pflag",
|
||||
|
@ -675,6 +677,7 @@
|
|||
"github.com/tendermint/tendermint/libs/db",
|
||||
"github.com/tendermint/tendermint/libs/log",
|
||||
"github.com/tendermint/tendermint/lite",
|
||||
"github.com/tendermint/tendermint/lite/errors",
|
||||
"github.com/tendermint/tendermint/lite/proxy",
|
||||
"github.com/tendermint/tendermint/node",
|
||||
"github.com/tendermint/tendermint/p2p",
|
||||
|
@ -686,10 +689,8 @@
|
|||
"github.com/tendermint/tendermint/rpc/lib/server",
|
||||
"github.com/tendermint/tendermint/types",
|
||||
"github.com/tendermint/tendermint/version",
|
||||
"github.com/tendermint/tmlibs/cli",
|
||||
"github.com/zondax/ledger-goclient",
|
||||
"golang.org/x/crypto/blowfish",
|
||||
"golang.org/x/crypto/ripemd160",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
[[override]]
|
||||
name = "github.com/tendermint/iavl"
|
||||
version = "=v0.10.0"
|
||||
version = "=v0.11.0"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/tendermint/tendermint"
|
||||
|
@ -70,3 +70,7 @@
|
|||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
version = "1.0.0"
|
||||
|
|
18
Makefile
18
Makefile
|
@ -1,8 +1,8 @@
|
|||
PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation')
|
||||
PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation')
|
||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||
VERSION := $(shell git describe --tags --long | sed 's/v\(.*\)/\1/')
|
||||
BUILD_TAGS = netgo ledger
|
||||
BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
||||
BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.Version=${VERSION}"
|
||||
GCC := $(shell command -v gcc 2> /dev/null)
|
||||
LEDGER_ENABLED ?= true
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
@ -142,10 +142,10 @@ test_examples:
|
|||
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/examples/democoin/cli_test` -tags=cli_test
|
||||
|
||||
test_unit:
|
||||
@go test $(PACKAGES_NOSIMULATION)
|
||||
@VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION)
|
||||
|
||||
test_race:
|
||||
@go test -race $(PACKAGES_NOSIMULATION)
|
||||
@VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION)
|
||||
|
||||
test_sim_modules:
|
||||
@echo "Running individual module simulations..."
|
||||
|
@ -157,11 +157,11 @@ test_sim_gaia_nondeterminism:
|
|||
|
||||
test_sim_gaia_fast:
|
||||
@echo "Running quick Gaia simulation. This may take several minutes..."
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=150 -v -timeout 24h
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h
|
||||
|
||||
test_sim_gaia_slow:
|
||||
@echo "Running full Gaia simulation. This may take awhile!"
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -v -timeout 24h
|
||||
test_sim_gaia_full:
|
||||
@echo "Running full multi-seed Gaia simulation. This may take awhile!"
|
||||
@sh scripts/multisim.sh
|
||||
|
||||
SIM_NUM_BLOCKS ?= 210
|
||||
SIM_BLOCK_SIZE ?= 200
|
||||
|
@ -175,7 +175,7 @@ test_sim_gaia_profile:
|
|||
@go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
@export VERSION=$(VERSION); bash tests/test_cover.sh
|
||||
|
||||
test_lint:
|
||||
gometalinter.v2 --config=tools/gometalinter.json ./...
|
||||
|
|
55
PENDING.md
55
PENDING.md
|
@ -4,6 +4,7 @@ BREAKING CHANGES
|
|||
|
||||
* Gaia REST API (`gaiacli advanced rest-server`)
|
||||
* [x/stake] Validator.Owner renamed to Validator.Operator
|
||||
* [\#595](https://github.com/cosmos/cosmos-sdk/issues/595) Connections to the REST server are now secured using Transport Layer Security by default. The --insecure flag is provided to switch back to insecure HTTP.
|
||||
|
||||
* Gaia CLI (`gaiacli`)
|
||||
* [x/stake] Validator.Owner renamed to Validator.Operator
|
||||
|
@ -17,6 +18,8 @@ BREAKING CHANGES
|
|||
utilize a validator's operator address must now use the new Bech32 prefix,
|
||||
`cosmosvaloper`.
|
||||
* [cli] [\#2190](https://github.com/cosmos/cosmos-sdk/issues/2190) `gaiacli init --gen-txs` is now `gaiacli init --with-txs` to reduce confusion
|
||||
* [cli] \#2073 --from can now be either an address or a key name
|
||||
* [cli] [\#1184](https://github.com/cosmos/cosmos-sdk/issues/1184) Subcommands reorganisation, see [\#2390](https://github.com/cosmos/cosmos-sdk/pull/2390) for a comprehensive list of changes.
|
||||
|
||||
* Gaia
|
||||
* Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013)
|
||||
|
@ -33,6 +36,9 @@ BREAKING CHANGES
|
|||
renamed for accounts and validator operators:
|
||||
* `cosmosaccaddr` / `cosmosaccpub` => `cosmos` / `cosmospub`
|
||||
* `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub`
|
||||
* [x/stake] [#1013] TendermintUpdates now uses transient store
|
||||
* [x/gov] [#2195] Governance uses BFT Time
|
||||
* [x/gov] \#2256 Removed slashing for governance non-voting validators
|
||||
|
||||
* SDK
|
||||
* [core] \#2219 Update to Tendermint 0.24.0
|
||||
|
@ -42,14 +48,24 @@ BREAKING CHANGES
|
|||
* [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal
|
||||
* [types] [\#1901](https://github.com/cosmos/cosmos-sdk/issues/1901) Validator interface's GetOwner() renamed to GetOperator()
|
||||
* [x/slashing] [#2122](https://github.com/cosmos/cosmos-sdk/pull/2122) - Implement slashing period
|
||||
* [types] [\#2119](https://github.com/cosmos/cosmos-sdk/issues/2119) Parsed error messages and ABCI log errors to make them more human readable.
|
||||
* [types] [\#2119](https://github.com/cosmos/cosmos-sdk/issues/2119) Parsed error messages and ABCI log errors to make them more human readable.
|
||||
* [types] \#2407 MulInt method added to big decimal in order to improve efficiency of slashing
|
||||
* [simulation] Rename TestAndRunTx to Operation [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153)
|
||||
* [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282
|
||||
* [simulation] Remove usage of keys and addrs in the types, in favor of simulation.Account \#2384
|
||||
* [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211)
|
||||
* [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441)
|
||||
* [baseapp] [\#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) Add minimumFees field to BaseApp.
|
||||
* [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308
|
||||
* [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New().
|
||||
* [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging.
|
||||
* [baseapp] \#2366 Automatically add action tags to all messages
|
||||
* [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] \#2236 more distribution hooks for distribution
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
||||
FEATURES
|
||||
|
||||
* Gaia REST API (`gaiacli advanced rest-server`)
|
||||
|
@ -64,21 +80,32 @@ FEATURES
|
|||
* [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in
|
||||
* [\#2040](https://github.com/cosmos/cosmos-sdk/issues/2040) Add `--bech` to `gaiacli keys show` and respective REST endpoint to
|
||||
provide desired Bech32 prefix encoding
|
||||
* [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution.
|
||||
* [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0.
|
||||
* [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) [\#2306](https://github.com/cosmos/cosmos-sdk/pull/2306) Passing --gas=simulate triggers a simulation of the tx before the actual execution.
|
||||
The gas estimate obtained via the simulation will be used as gas limit in the actual execution.
|
||||
* [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=simulate.
|
||||
* [cli] [\#2110](https://github.com/cosmos/cosmos-sdk/issues/2110) Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated.
|
||||
* [cli] [\#2204](https://github.com/cosmos/cosmos-sdk/issues/2204) Support generating and broadcasting messages with multiple signatures via command line:
|
||||
* [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT.
|
||||
* [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag.
|
||||
* [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command.
|
||||
* [cli] \#2220 Add `gaiacli config` feature to interactively create CLI config files to reduce the number of required flags
|
||||
* [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced
|
||||
new commission flags for validator commands `create-validator` and `edit-validator`.
|
||||
|
||||
* Gaia
|
||||
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
|
||||
* [simulation] #2313 Reworked `make test_sim_gaia_slow` to `make test_sim_gaia_full`, now simulates from multiple starting seeds in parallel
|
||||
* [cli] [\#1921] (https://github.com/cosmos/cosmos-sdk/issues/1921)
|
||||
* New configuration file `gaiad.toml` is now created to host Gaia-specific configuration.
|
||||
* New --minimum_fees/minimum_fees flag/config option to set a minimum fee.
|
||||
|
||||
* SDK
|
||||
* [querier] added custom querier functionality, so ABCI query requests can be handled by keepers
|
||||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) allow operations to specify future operations
|
||||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile"
|
||||
* [simulation] [\#2349](https://github.com/cosmos/cosmos-sdk/issues/2349) Add time-based future scheduled operations to simulator
|
||||
* [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement
|
||||
basis for the validator commission model.
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
@ -99,14 +126,27 @@ IMPROVEMENTS
|
|||
* [x/auth] Signature verification's gas cost now accounts for pubkey type. [#2046](https://github.com/tendermint/tendermint/pull/2046)
|
||||
* [x/stake] [x/slashing] Ensure delegation invariants to jailed validators [#1883](https://github.com/cosmos/cosmos-sdk/issues/1883).
|
||||
* [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200)
|
||||
* [genesis] \#2229 Ensure that there are no duplicate accounts 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
|
||||
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
||||
|
||||
* SDK
|
||||
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
|
||||
* [spec] Added simple piggy bank distribution spec
|
||||
* [cli] [\#1632](https://github.com/cosmos/cosmos-sdk/issues/1632) Add integration tests to ensure `basecoind init && basecoind` start sequences run successfully for both `democoin` and `basecoin` examples.
|
||||
* [store] Speedup IAVL iteration, and consequently everything that requires IAVL iteration. [#2143](https://github.com/cosmos/cosmos-sdk/issues/2143)
|
||||
* [store] \#1952 Update IAVL dependency to v0.10.0
|
||||
* [store] \#1952, \#2281 Update IAVL dependency to v0.11.0
|
||||
* [simulation] Make timestamps randomized [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153)
|
||||
* [simulation] Make logs not just pure strings, speeding it up by a large factor at greater block heights \#2282
|
||||
* [simulation] Add a concept of weighting the operations \#2303
|
||||
* [simulation] Logs get written to file if large, and also get printed on panics \#2285
|
||||
* [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable
|
||||
* [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149)
|
||||
* [store] [\#2017](https://github.com/cosmos/cosmos-sdk/issues/2017) Refactor
|
||||
gas iterator gas consumption to only consume gas for iterator creation and `Next`
|
||||
calls which includes dynamic consumption of value length.
|
||||
* [types/decimal] \#2378 - Added truncate functionality to decimal
|
||||
* [client] [\#1184](https://github.com/cosmos/cosmos-sdk/issues/1184) Remove unused `client/tx/sign.go`.
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
@ -119,6 +159,8 @@ BUG FIXES
|
|||
* [cli] [\#2265](https://github.com/cosmos/cosmos-sdk/issues/2265) Fix JSON formatting of the `gaiacli send` command.
|
||||
|
||||
* Gaia
|
||||
* [x/stake] Return correct Tendermint validator update set on `EndBlocker` by not
|
||||
including non previously bonded validators that have zero power. [#2189](https://github.com/cosmos/cosmos-sdk/issues/2189)
|
||||
|
||||
* SDK
|
||||
* [\#1988](https://github.com/cosmos/cosmos-sdk/issues/1988) Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988)
|
||||
|
@ -127,5 +169,6 @@ BUG FIXES
|
|||
loading a Ledger device at runtime.
|
||||
* [\#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
|
||||
* [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository.
|
||||
|
||||
* Tendermint
|
||||
|
|
|
@ -14,10 +14,10 @@ import (
|
|||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Key to store the header in the DB itself.
|
||||
|
@ -68,6 +68,9 @@ type BaseApp struct {
|
|||
deliverState *state // for DeliverTx
|
||||
voteInfos []abci.VoteInfo // absent validators from begin block
|
||||
|
||||
// minimum fees for spam prevention
|
||||
minimumFees sdk.Coins
|
||||
|
||||
// flag for sealing
|
||||
sealed bool
|
||||
}
|
||||
|
@ -120,13 +123,20 @@ func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.Codespace
|
|||
return app.codespacer.RegisterNext(codespace)
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore
|
||||
// Mount IAVL stores to the provided keys in the BaseApp multistore
|
||||
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
|
||||
for _, key := range keys {
|
||||
app.MountStore(key, sdk.StoreTypeIAVL)
|
||||
}
|
||||
}
|
||||
|
||||
// Mount stores to the provided keys in the BaseApp multistore
|
||||
func (app *BaseApp) MountStoresTransient(keys ...*sdk.TransientStoreKey) {
|
||||
for _, key := range keys {
|
||||
app.MountStore(key, sdk.StoreTypeTransient)
|
||||
}
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore, using a specified DB
|
||||
func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
|
||||
app.cms.MountStoreWithDB(key, typ, db)
|
||||
|
@ -181,10 +191,13 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetMinimumFees sets the minimum fees.
|
||||
func (app *BaseApp) SetMinimumFees(fees sdk.Coins) { app.minimumFees = fees }
|
||||
|
||||
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
|
||||
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
|
||||
if isCheckTx {
|
||||
return sdk.NewContext(app.checkState.ms, header, true, app.Logger)
|
||||
return sdk.NewContext(app.checkState.ms, header, true, app.Logger).WithMinimumFees(app.minimumFees)
|
||||
}
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, app.Logger)
|
||||
}
|
||||
|
@ -202,7 +215,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
|
|||
ms := app.cms.CacheMultiStore()
|
||||
app.checkState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, true, app.Logger),
|
||||
ctx: sdk.NewContext(ms, header, true, app.Logger).WithMinimumFees(app.minimumFees),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +337,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
|
|||
}
|
||||
|
||||
// Encode with json
|
||||
value := wire.Cdc.MustMarshalBinary(result)
|
||||
value := codec.Cdc.MustMarshalBinary(result)
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.ABCICodeOK),
|
||||
Value: value,
|
||||
|
@ -379,7 +392,8 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
|||
sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult()
|
||||
}
|
||||
|
||||
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger)
|
||||
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
|
||||
WithMinimumFees(app.minimumFees)
|
||||
// Passes the rest of the path as an argument to the querier.
|
||||
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
|
||||
resBytes, err := querier(ctx, path[2:], req)
|
||||
|
@ -525,6 +539,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
|
|||
if mode != runTxModeCheck {
|
||||
msgResult = handler(ctx, msg)
|
||||
}
|
||||
msgResult.Tags = append(msgResult.Tags, sdk.MakeTag("action", []byte(msg.Name())))
|
||||
|
||||
// NOTE: GasWanted is determined by ante handler and
|
||||
// GasUsed by the GasMeter
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -34,14 +34,14 @@ func defaultLogger() log.Logger {
|
|||
func newBaseApp(name string, options ...func(*BaseApp)) *BaseApp {
|
||||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
codec := wire.NewCodec()
|
||||
codec := codec.New()
|
||||
registerTestCodec(codec)
|
||||
return NewBaseApp(name, logger, db, testTxDecoder(codec), options...)
|
||||
}
|
||||
|
||||
func registerTestCodec(cdc *wire.Codec) {
|
||||
func registerTestCodec(cdc *codec.Codec) {
|
||||
// register Tx, Msg
|
||||
sdk.RegisterWire(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
|
||||
// register test types
|
||||
cdc.RegisterConcrete(&txTest{}, "cosmos-sdk/baseapp/txTest", nil)
|
||||
|
@ -302,6 +302,7 @@ type msgCounter struct {
|
|||
|
||||
// Implements Msg
|
||||
func (msg msgCounter) Type() string { return typeMsgCounter }
|
||||
func (msg msgCounter) Name() string { return "counter1" }
|
||||
func (msg msgCounter) GetSignBytes() []byte { return nil }
|
||||
func (msg msgCounter) GetSigners() []sdk.AccAddress { return nil }
|
||||
func (msg msgCounter) ValidateBasic() sdk.Error {
|
||||
|
@ -340,6 +341,7 @@ type msgCounter2 struct {
|
|||
|
||||
// Implements Msg
|
||||
func (msg msgCounter2) Type() string { return typeMsgCounter2 }
|
||||
func (msg msgCounter2) Name() string { return "counter2" }
|
||||
func (msg msgCounter2) GetSignBytes() []byte { return nil }
|
||||
func (msg msgCounter2) GetSigners() []sdk.AccAddress { return nil }
|
||||
func (msg msgCounter2) ValidateBasic() sdk.Error {
|
||||
|
@ -350,7 +352,7 @@ func (msg msgCounter2) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
// amino decode
|
||||
func testTxDecoder(cdc *wire.Codec) sdk.TxDecoder {
|
||||
func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx txTest
|
||||
if len(txBytes) == 0 {
|
||||
|
@ -448,7 +450,7 @@ func TestCheckTx(t *testing.T) {
|
|||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// Create same codec used in txDecoder
|
||||
codec := wire.NewCodec()
|
||||
codec := codec.New()
|
||||
registerTestCodec(codec)
|
||||
|
||||
for i := int64(0); i < nTxs; i++ {
|
||||
|
@ -489,7 +491,7 @@ func TestDeliverTx(t *testing.T) {
|
|||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
// Create same codec used in txDecoder
|
||||
codec := wire.NewCodec()
|
||||
codec := codec.New()
|
||||
registerTestCodec(codec)
|
||||
|
||||
nBlocks := 3
|
||||
|
@ -532,7 +534,7 @@ func TestMultiMsgDeliverTx(t *testing.T) {
|
|||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
// Create same codec used in txDecoder
|
||||
codec := wire.NewCodec()
|
||||
codec := codec.New()
|
||||
registerTestCodec(codec)
|
||||
|
||||
// run a multi-msg tx
|
||||
|
@ -613,8 +615,8 @@ func TestSimulateTx(t *testing.T) {
|
|||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// Create same codec used in txDecoder
|
||||
codec := wire.NewCodec()
|
||||
registerTestCodec(codec)
|
||||
cdc := codec.New()
|
||||
registerTestCodec(cdc)
|
||||
|
||||
nBlocks := 3
|
||||
for blockN := 0; blockN < nBlocks; blockN++ {
|
||||
|
@ -634,7 +636,7 @@ func TestSimulateTx(t *testing.T) {
|
|||
require.Equal(t, gasConsumed, result.GasUsed)
|
||||
|
||||
// simulate by calling Query with encoded tx
|
||||
txBytes, err := codec.MarshalBinary(tx)
|
||||
txBytes, err := cdc.MarshalBinary(tx)
|
||||
require.Nil(t, err)
|
||||
query := abci.RequestQuery{
|
||||
Path: "/app/simulate",
|
||||
|
@ -644,7 +646,7 @@ func TestSimulateTx(t *testing.T) {
|
|||
require.True(t, queryResult.IsOK(), queryResult.Log)
|
||||
|
||||
var res sdk.Result
|
||||
wire.Cdc.MustUnmarshalBinary(queryResult.Value, &res)
|
||||
codec.Cdc.MustUnmarshalBinary(queryResult.Value, &res)
|
||||
require.Nil(t, err, "Result unmarshalling failed")
|
||||
require.True(t, res.IsOK(), res.Log)
|
||||
require.Equal(t, gasConsumed, res.GasUsed, res.Log)
|
||||
|
@ -721,7 +723,7 @@ func TestRunInvalidTransaction(t *testing.T) {
|
|||
tx.Msgs = append(tx.Msgs, msgNoDecode{})
|
||||
|
||||
// new codec so we can encode the tx, but we shouldn't be able to decode
|
||||
newCdc := wire.NewCodec()
|
||||
newCdc := codec.New()
|
||||
registerTestCodec(newCdc)
|
||||
newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil)
|
||||
|
||||
|
@ -819,92 +821,3 @@ func TestTxGasLimits(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
// Queries
|
||||
|
||||
// Test that we can only query from the latest committed state.
|
||||
func TestQuery(t *testing.T) {
|
||||
key, value := []byte("hello"), []byte("goodbye")
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// NOTE: "/store/key1" tells us KVStore
|
||||
// and the final "/key" says to use the data as the
|
||||
// key in the given KVStore ...
|
||||
query := abci.RequestQuery{
|
||||
Path: "/store/key1/key",
|
||||
Data: key,
|
||||
}
|
||||
tx := newTxCounter(0, 0)
|
||||
|
||||
// query is empty before we do anything
|
||||
res := app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a CheckTx
|
||||
resTx := app.Check(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a DeliverTx before we commit
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
resTx = app.Deliver(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query returns correct value after Commit
|
||||
app.Commit()
|
||||
res = app.Query(query)
|
||||
require.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
// Test p2p filter queries
|
||||
func TestP2PQuery(t *testing.T) {
|
||||
addrPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
|
||||
require.Equal(t, "1.1.1.1:8000", addrport)
|
||||
return abci.ResponseQuery{Code: uint32(3)}
|
||||
})
|
||||
}
|
||||
|
||||
pubkeyPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
|
||||
require.Equal(t, "testpubkey", pubkey)
|
||||
return abci.ResponseQuery{Code: uint32(4)}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt)
|
||||
|
||||
addrQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/addr/1.1.1.1:8000",
|
||||
}
|
||||
res := app.Query(addrQuery)
|
||||
require.Equal(t, uint32(3), res.Code)
|
||||
|
||||
pubkeyQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/pubkey/testpubkey",
|
||||
}
|
||||
res = app.Query(pubkeyQuery)
|
||||
require.Equal(t, uint32(4), res.Code)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
// nolint: golint
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
)
|
||||
|
||||
// File for storing in-package BaseApp optional functions,
|
||||
|
@ -20,9 +23,100 @@ func SetPruning(pruning string) func(*BaseApp) {
|
|||
case "syncable":
|
||||
pruningEnum = sdk.PruneSyncable
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid pruning strategy: %s", pruning))
|
||||
panic(fmt.Sprintf("invalid pruning strategy: %s", pruning))
|
||||
}
|
||||
return func(bap *BaseApp) {
|
||||
bap.cms.SetPruning(pruningEnum)
|
||||
}
|
||||
}
|
||||
|
||||
// SetMinimumFees returns an option that sets the minimum fees on the app.
|
||||
func SetMinimumFees(minFees string) func(*BaseApp) {
|
||||
fees, err := sdk.ParseCoins(minFees)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid minimum fees: %v", err))
|
||||
}
|
||||
return func(bap *BaseApp) { bap.SetMinimumFees(fees) }
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetName(name string) {
|
||||
if app.sealed {
|
||||
panic("SetName() on sealed BaseApp")
|
||||
}
|
||||
app.name = name
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetDB(db dbm.DB) {
|
||||
if app.sealed {
|
||||
panic("SetDB() on sealed BaseApp")
|
||||
}
|
||||
app.db = db
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetCMS(cms store.CommitMultiStore) {
|
||||
if app.sealed {
|
||||
panic("SetEndBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.cms = cms
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
|
||||
if app.sealed {
|
||||
panic("SetInitChainer() on sealed BaseApp")
|
||||
}
|
||||
app.initChainer = initChainer
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) {
|
||||
if app.sealed {
|
||||
panic("SetBeginBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.beginBlocker = beginBlocker
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
|
||||
if app.sealed {
|
||||
panic("SetEndBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.endBlocker = endBlocker
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
|
||||
if app.sealed {
|
||||
panic("SetAnteHandler() on sealed BaseApp")
|
||||
}
|
||||
app.anteHandler = ah
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) {
|
||||
if app.sealed {
|
||||
panic("SetAddrPeerFilter() on sealed BaseApp")
|
||||
}
|
||||
app.addrPeerFilter = pf
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) {
|
||||
if app.sealed {
|
||||
panic("SetPubKeyPeerFilter() on sealed BaseApp")
|
||||
}
|
||||
app.pubkeyPeerFilter = pf
|
||||
}
|
||||
|
||||
func (app *BaseApp) Router() Router {
|
||||
if app.sealed {
|
||||
panic("Router() on sealed BaseApp")
|
||||
}
|
||||
return app.router
|
||||
}
|
||||
|
||||
func (app *BaseApp) QueryRouter() QueryRouter {
|
||||
return app.queryRouter
|
||||
}
|
||||
|
||||
func (app *BaseApp) Seal() { app.sealed = true }
|
||||
func (app *BaseApp) IsSealed() bool { return app.sealed }
|
||||
func (app *BaseApp) enforceSeal() {
|
||||
if !app.sealed {
|
||||
panic("enforceSeal() on BaseApp but not sealed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// Test that we can only query from the latest committed state.
|
||||
func TestQuery(t *testing.T) {
|
||||
key, value := []byte("hello"), []byte("goodbye")
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// NOTE: "/store/key1" tells us KVStore
|
||||
// and the final "/key" says to use the data as the
|
||||
// key in the given KVStore ...
|
||||
query := abci.RequestQuery{
|
||||
Path: "/store/key1/key",
|
||||
Data: key,
|
||||
}
|
||||
tx := newTxCounter(0, 0)
|
||||
|
||||
// query is empty before we do anything
|
||||
res := app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a CheckTx
|
||||
resTx := app.Check(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a DeliverTx before we commit
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
resTx = app.Deliver(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query returns correct value after Commit
|
||||
app.Commit()
|
||||
res = app.Query(query)
|
||||
require.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
// Test p2p filter queries
|
||||
func TestP2PQuery(t *testing.T) {
|
||||
addrPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
|
||||
require.Equal(t, "1.1.1.1:8000", addrport)
|
||||
return abci.ResponseQuery{Code: uint32(3)}
|
||||
})
|
||||
}
|
||||
|
||||
pubkeyPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
|
||||
require.Equal(t, "testpubkey", pubkey)
|
||||
return abci.ResponseQuery{Code: uint32(4)}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt)
|
||||
|
||||
addrQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/addr/1.1.1.1:8000",
|
||||
}
|
||||
res := app.Query(addrQuery)
|
||||
require.Equal(t, uint32(3), res.Code)
|
||||
|
||||
pubkeyQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/pubkey/testpubkey",
|
||||
}
|
||||
res = app.Query(pubkeyQuery)
|
||||
require.Equal(t, uint32(4), res.Code)
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// nolint - Setter functions
|
||||
func (app *BaseApp) SetName(name string) {
|
||||
if app.sealed {
|
||||
panic("SetName() on sealed BaseApp")
|
||||
}
|
||||
app.name = name
|
||||
}
|
||||
func (app *BaseApp) SetDB(db dbm.DB) {
|
||||
if app.sealed {
|
||||
panic("SetDB() on sealed BaseApp")
|
||||
}
|
||||
app.db = db
|
||||
}
|
||||
func (app *BaseApp) SetCMS(cms store.CommitMultiStore) {
|
||||
if app.sealed {
|
||||
panic("SetEndBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.cms = cms
|
||||
}
|
||||
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
|
||||
if app.sealed {
|
||||
panic("SetInitChainer() on sealed BaseApp")
|
||||
}
|
||||
app.initChainer = initChainer
|
||||
}
|
||||
func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) {
|
||||
if app.sealed {
|
||||
panic("SetBeginBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.beginBlocker = beginBlocker
|
||||
}
|
||||
func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
|
||||
if app.sealed {
|
||||
panic("SetEndBlocker() on sealed BaseApp")
|
||||
}
|
||||
app.endBlocker = endBlocker
|
||||
}
|
||||
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
|
||||
if app.sealed {
|
||||
panic("SetAnteHandler() on sealed BaseApp")
|
||||
}
|
||||
app.anteHandler = ah
|
||||
}
|
||||
func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) {
|
||||
if app.sealed {
|
||||
panic("SetAddrPeerFilter() on sealed BaseApp")
|
||||
}
|
||||
app.addrPeerFilter = pf
|
||||
}
|
||||
func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) {
|
||||
if app.sealed {
|
||||
panic("SetPubKeyPeerFilter() on sealed BaseApp")
|
||||
}
|
||||
app.pubkeyPeerFilter = pf
|
||||
}
|
||||
func (app *BaseApp) Router() Router {
|
||||
if app.sealed {
|
||||
panic("Router() on sealed BaseApp")
|
||||
}
|
||||
return app.router
|
||||
}
|
||||
func (app *BaseApp) QueryRouter() QueryRouter {
|
||||
return app.queryRouter
|
||||
}
|
||||
func (app *BaseApp) Seal() { app.sealed = true }
|
||||
func (app *BaseApp) IsSealed() bool { return app.sealed }
|
||||
func (app *BaseApp) enforceSeal() {
|
||||
if !app.sealed {
|
||||
panic("enforceSeal() on BaseApp but not sealed")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"bufio"
|
||||
"path"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"github.com/pelletier/go-toml"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type cliConfig struct {
|
||||
Home string `toml:"home"`
|
||||
ChainID string `toml:"chain_id"`
|
||||
TrustNode bool `toml:"trust_node"`
|
||||
Encoding string `toml:"encoding"`
|
||||
Output string `toml:"output"`
|
||||
Node string `toml:"node"`
|
||||
Trace bool `toml:"trace"`
|
||||
}
|
||||
|
||||
// ConfigCmd returns a CLI command to interactively create a
|
||||
// Gaia CLI config file.
|
||||
func ConfigCmd() *cobra.Command {
|
||||
cfg := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Interactively creates a Gaia CLI config file",
|
||||
RunE: runConfigCmd,
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func runConfigCmd(cmd *cobra.Command, args [] string) error {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdin := BufferStdin()
|
||||
gaiaCLIHome, err := handleGaiaCLIHome(home, stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node, err := handleNode(stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trustNode, err := handleTrustNode(stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encoding := "btc"
|
||||
output := "text"
|
||||
var chainID string
|
||||
chainID, err = types.DefaultChainID()
|
||||
if err != nil {
|
||||
fmt.Println("Couldn't populate ChainID, so using an empty one.")
|
||||
}
|
||||
|
||||
cfg := &cliConfig{
|
||||
Home: gaiaCLIHome,
|
||||
ChainID: chainID,
|
||||
TrustNode: trustNode,
|
||||
Encoding: encoding,
|
||||
Output: output,
|
||||
Node: node,
|
||||
Trace: false,
|
||||
}
|
||||
|
||||
return createGaiaCLIConfig(cfg)
|
||||
}
|
||||
|
||||
func handleGaiaCLIHome(dir string, stdin *bufio.Reader) (string, error) {
|
||||
dirName := ".gaiacli"
|
||||
home, err := GetString(fmt.Sprintf("Where is your gaiacli home directory? (Default: ~/%s)", dirName), stdin)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if home == "" {
|
||||
home = path.Join(dir, dirName)
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
||||
|
||||
func handleNode(stdin *bufio.Reader) (string, error) {
|
||||
defaultNode := "tcp://localhost:26657"
|
||||
node, err := GetString(fmt.Sprintf("Where is your validator node running? (Default: %s)", defaultNode), stdin)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if node == "" {
|
||||
node = defaultNode
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func handleTrustNode(stdin *bufio.Reader) (bool, error) {
|
||||
return GetConfirmation("Do you trust this node?", stdin)
|
||||
}
|
||||
|
||||
func createGaiaCLIConfig(cfg *cliConfig) error {
|
||||
cfgPath := path.Join(cfg.Home, "config")
|
||||
err := os.MkdirAll(cfgPath, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := toml.Marshal(*cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgFile := path.Join(cfgPath, "config.toml")
|
||||
if info, err := os.Stat(cfgFile); err == nil && !info.IsDir() {
|
||||
err = os.Rename(cfgFile, path.Join(cfgPath, "config.toml-old"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(cfgFile, data, os.ModePerm)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
// TODO: This should get deleted eventually, and perhaps
|
||||
// ctypes.ResultBroadcastTx be stripped of unused fields, and
|
||||
// ctypes.ResultBroadcastTxCommit returned for tendermint RPC BroadcastTxSync.
|
||||
//
|
||||
// The motivation is that we want a unified type to return, and the better
|
||||
// option is the one that can hold CheckTx/DeliverTx responses optionally.
|
||||
func resultBroadcastTxToCommit(res *ctypes.ResultBroadcastTx) *ctypes.ResultBroadcastTxCommit {
|
||||
return &ctypes.ResultBroadcastTxCommit{
|
||||
Hash: res.Hash,
|
||||
// NOTE: other fields are unused for async.
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastTx broadcasts a transactions either synchronously or asynchronously
|
||||
// based on the context parameters. The result of the broadcast is parsed into
|
||||
// an intermediate structure which is logged if the context has a logger
|
||||
// defined.
|
||||
func (ctx CLIContext) BroadcastTx(txBytes []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
if ctx.Async {
|
||||
res, err := ctx.broadcastTxAsync(txBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resCommit := resultBroadcastTxToCommit(res)
|
||||
return resCommit, err
|
||||
}
|
||||
|
||||
return ctx.broadcastTxCommit(txBytes)
|
||||
}
|
||||
|
||||
// BroadcastTxAndAwaitCommit broadcasts transaction bytes to a Tendermint node
|
||||
// and waits for a commit.
|
||||
func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.BroadcastTxCommit(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !res.CheckTx.IsOK() {
|
||||
return res, errors.Errorf("checkTx failed: (%d) %s",
|
||||
res.CheckTx.Code,
|
||||
res.CheckTx.Log)
|
||||
}
|
||||
|
||||
if !res.DeliverTx.IsOK() {
|
||||
return res, errors.Errorf("deliverTx failed: (%d) %s",
|
||||
res.DeliverTx.Code,
|
||||
res.DeliverTx.Log)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node
|
||||
// asynchronously.
|
||||
func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.BroadcastTxAsync(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (ctx CLIContext) broadcastTxAsync(txBytes []byte) (*ctypes.ResultBroadcastTx, error) {
|
||||
res, err := ctx.BroadcastTxAsync(txBytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
if ctx.JSON {
|
||||
type toJSON struct {
|
||||
TxHash string
|
||||
}
|
||||
|
||||
resJSON := toJSON{res.Hash.String()}
|
||||
bz, err := ctx.Codec.MarshalJSON(resJSON)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
ctx.Logger.Write(bz)
|
||||
io.WriteString(ctx.Logger, "\n")
|
||||
} else {
|
||||
io.WriteString(ctx.Logger, fmt.Sprintf("async tx sent (tx hash: %s)\n", res.Hash))
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ctx CLIContext) broadcastTxCommit(txBytes []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
res, err := ctx.BroadcastTxAndAwaitCommit(txBytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if ctx.JSON {
|
||||
// Since JSON is intended for automated scripts, always include response in
|
||||
// JSON mode.
|
||||
type toJSON struct {
|
||||
Height int64
|
||||
TxHash string
|
||||
Response abci.ResponseDeliverTx
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resJSON := toJSON{res.Height, res.Hash.String(), res.DeliverTx}
|
||||
bz, err := ctx.Codec.MarshalJSON(resJSON)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
ctx.Logger.Write(bz)
|
||||
io.WriteString(ctx.Logger, "\n")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resStr := fmt.Sprintf("Committed at block %d (tx hash: %s)\n", res.Height, res.Hash.String())
|
||||
|
||||
if ctx.PrintResponse {
|
||||
resStr = fmt.Sprintf("Committed at block %d (tx hash: %s, response: %+v)\n",
|
||||
res.Height, res.Hash.String(), res.DeliverTx,
|
||||
)
|
||||
}
|
||||
|
||||
io.WriteString(ctx.Logger, resStr)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
|
@ -6,16 +6,20 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
cskeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmlite "github.com/tendermint/tendermint/lite"
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
"os"
|
||||
)
|
||||
|
||||
const ctxAccStoreName = "acc"
|
||||
|
@ -23,24 +27,24 @@ const ctxAccStoreName = "acc"
|
|||
// CLIContext implements a typical CLI context created in SDK modules for
|
||||
// transaction handling and queries.
|
||||
type CLIContext struct {
|
||||
Codec *wire.Codec
|
||||
AccDecoder auth.AccountDecoder
|
||||
Client rpcclient.Client
|
||||
Logger io.Writer
|
||||
Height int64
|
||||
Gas int64
|
||||
GasAdjustment float64
|
||||
NodeURI string
|
||||
FromAddressName string
|
||||
AccountStore string
|
||||
TrustNode bool
|
||||
UseLedger bool
|
||||
Async bool
|
||||
JSON bool
|
||||
PrintResponse bool
|
||||
Verifier tmlite.Verifier
|
||||
DryRun bool
|
||||
GenerateOnly bool
|
||||
Codec *codec.Codec
|
||||
AccDecoder auth.AccountDecoder
|
||||
Client rpcclient.Client
|
||||
Logger io.Writer
|
||||
Height int64
|
||||
NodeURI string
|
||||
From string
|
||||
AccountStore string
|
||||
TrustNode bool
|
||||
UseLedger bool
|
||||
Async bool
|
||||
JSON bool
|
||||
PrintResponse bool
|
||||
Verifier tmlite.Verifier
|
||||
DryRun bool
|
||||
GenerateOnly bool
|
||||
fromAddress types.AccAddress
|
||||
fromName string
|
||||
}
|
||||
|
||||
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
||||
|
@ -53,59 +57,103 @@ func NewCLIContext() CLIContext {
|
|||
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
|
||||
}
|
||||
|
||||
from := viper.GetString(client.FlagFrom)
|
||||
fromAddress, fromName := fromFields(from)
|
||||
|
||||
return CLIContext{
|
||||
Client: rpc,
|
||||
NodeURI: nodeURI,
|
||||
AccountStore: ctxAccStoreName,
|
||||
FromAddressName: viper.GetString(client.FlagFrom),
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
Gas: viper.GetInt64(client.FlagGas),
|
||||
GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment),
|
||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
UseLedger: viper.GetBool(client.FlagUseLedger),
|
||||
Async: viper.GetBool(client.FlagAsync),
|
||||
JSON: viper.GetBool(client.FlagJson),
|
||||
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
||||
Verifier: createVerifier(),
|
||||
DryRun: viper.GetBool(client.FlagDryRun),
|
||||
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
||||
Client: rpc,
|
||||
NodeURI: nodeURI,
|
||||
AccountStore: ctxAccStoreName,
|
||||
From: viper.GetString(client.FlagFrom),
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
UseLedger: viper.GetBool(client.FlagUseLedger),
|
||||
Async: viper.GetBool(client.FlagAsync),
|
||||
JSON: viper.GetBool(client.FlagJson),
|
||||
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
||||
Verifier: createVerifier(),
|
||||
DryRun: viper.GetBool(client.FlagDryRun),
|
||||
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
||||
fromAddress: fromAddress,
|
||||
fromName: fromName,
|
||||
}
|
||||
}
|
||||
|
||||
func createVerifier() tmlite.Verifier {
|
||||
trustNodeDefined := viper.IsSet(client.FlagTrustNode)
|
||||
if !trustNodeDefined {
|
||||
return nil
|
||||
}
|
||||
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
if trustNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
home := viper.GetString(cli.HomeFlag)
|
||||
nodeURI := viper.GetString(client.FlagNode)
|
||||
|
||||
var errMsg bytes.Buffer
|
||||
if chainID == "" {
|
||||
errMsg.WriteString("chain-id ")
|
||||
errMsg.WriteString("--chain-id ")
|
||||
}
|
||||
if home == "" {
|
||||
errMsg.WriteString("home ")
|
||||
errMsg.WriteString("--home ")
|
||||
}
|
||||
if nodeURI == "" {
|
||||
errMsg.WriteString("node ")
|
||||
errMsg.WriteString("--node ")
|
||||
}
|
||||
// errMsg is not empty
|
||||
if errMsg.Len() != 0 {
|
||||
panic(fmt.Errorf("can't create certifier for distrust mode, empty values from these options: %s", errMsg.String()))
|
||||
fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String())
|
||||
os.Exit(1)
|
||||
}
|
||||
node := rpcclient.NewHTTP(nodeURI, "/websocket")
|
||||
// TODO Utilize ctx.Logger correctly
|
||||
verifier, err := tmliteProxy.NewVerifier(chainID, home, node, log.NewNopLogger())
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
fmt.Printf("Create verifier failed: %s\n", err.Error())
|
||||
fmt.Printf("Please check network connection and verify the address of the node to connect to\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return verifier
|
||||
}
|
||||
|
||||
func fromFields(from string) (fromAddr types.AccAddress, fromName string) {
|
||||
if from == "" {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
fmt.Println("no keybase found")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var info cskeys.Info
|
||||
if addr, err := types.AccAddressFromBech32(from); err == nil {
|
||||
info, err = keybase.GetByAddress(addr)
|
||||
if err != nil {
|
||||
fmt.Printf("could not find key %s\n", from)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
info, err = keybase.Get(from)
|
||||
if err != nil {
|
||||
fmt.Printf("could not find key %s\n", from)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fromAddr = info.GetAddress()
|
||||
fromName = info.GetName()
|
||||
return
|
||||
}
|
||||
|
||||
// WithCodec returns a copy of the context with an updated codec.
|
||||
func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext {
|
||||
func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext {
|
||||
ctx.Codec = cdc
|
||||
return ctx
|
||||
}
|
||||
|
@ -129,10 +177,9 @@ func (ctx CLIContext) WithAccountStore(accountStore string) CLIContext {
|
|||
return ctx
|
||||
}
|
||||
|
||||
// WithFromAddressName returns a copy of the context with an updated from
|
||||
// address.
|
||||
func (ctx CLIContext) WithFromAddressName(addrName string) CLIContext {
|
||||
ctx.FromAddressName = addrName
|
||||
// WithFrom returns a copy of the context with an updated from address or name.
|
||||
func (ctx CLIContext) WithFrom(from string) CLIContext {
|
||||
ctx.From = from
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
@ -167,9 +214,3 @@ func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext {
|
|||
ctx.Verifier = verifier
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithGasAdjustment returns a copy of the context with an updated GasAdjustment flag.
|
||||
func (ctx CLIContext) WithGasAdjustment(adjustment float64) CLIContext {
|
||||
ctx.GasAdjustment = adjustment
|
||||
return ctx
|
||||
}
|
||||
|
|
|
@ -11,3 +11,11 @@ func ErrInvalidAccount(addr sdk.AccAddress) error {
|
|||
return errors.Errorf(`No account with address %s was found in the state.
|
||||
Are you sure there has been a transaction involving it?`, addr)
|
||||
}
|
||||
|
||||
// ErrVerifyCommit returns a common error reflecting that the blockchain commit at a given
|
||||
// height can't be verified. The reason is that the base checkpoint of the certifier is
|
||||
// newer than the given height
|
||||
func ErrVerifyCommit(height int64) error {
|
||||
return errors.Errorf(`The height of base truststore in gaia-lite is higher than height %d.
|
||||
Can't verify blockchain proof at this height. Please set --trust-node to true and try again`, height)
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ package context
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
|
@ -12,13 +10,14 @@ import (
|
|||
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
tmliteErr "github.com/tendermint/tendermint/lite/errors"
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
// GetNode returns an RPC client. If the context's client is not defined, an
|
||||
|
@ -82,22 +81,13 @@ func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) {
|
|||
}
|
||||
|
||||
// GetFromAddress returns the from address from the context's name.
|
||||
func (ctx CLIContext) GetFromAddress() (from sdk.AccAddress, err error) {
|
||||
if ctx.FromAddressName == "" {
|
||||
return nil, errors.Errorf("must provide a from address name")
|
||||
}
|
||||
func (ctx CLIContext) GetFromAddress() (sdk.AccAddress, error) {
|
||||
return ctx.fromAddress, nil
|
||||
}
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := keybase.Get(ctx.FromAddressName)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("no key for: %s", ctx.FromAddressName)
|
||||
}
|
||||
|
||||
return sdk.AccAddress(info.GetPubKey().Address()), nil
|
||||
// GetFromName returns the key name for the current context.
|
||||
func (ctx CLIContext) GetFromName() (string, error) {
|
||||
return ctx.fromName, nil
|
||||
}
|
||||
|
||||
// GetAccountNumber returns the next account number for the given account
|
||||
|
@ -122,49 +112,6 @@ func (ctx CLIContext) GetAccountSequence(address []byte) (int64, error) {
|
|||
return account.GetSequence(), nil
|
||||
}
|
||||
|
||||
// BroadcastTx broadcasts transaction bytes to a Tendermint node.
|
||||
func (ctx CLIContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.BroadcastTxCommit(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !res.CheckTx.IsOK() {
|
||||
return res, errors.Errorf("checkTx failed: (%d) %s",
|
||||
res.CheckTx.Code,
|
||||
res.CheckTx.Log)
|
||||
}
|
||||
|
||||
if !res.DeliverTx.IsOK() {
|
||||
return res, errors.Errorf("deliverTx failed: (%d) %s",
|
||||
res.DeliverTx.Code,
|
||||
res.DeliverTx.Log)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node
|
||||
// asynchronously.
|
||||
func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) {
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.BroadcastTxAsync(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// EnsureAccountExists ensures that an account exists for a given context. An
|
||||
// error is returned if it does not.
|
||||
func (ctx CLIContext) EnsureAccountExists() error {
|
||||
|
@ -201,92 +148,6 @@ func (ctx CLIContext) EnsureAccountExistsFromAddr(addr sdk.AccAddress) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// EnsureBroadcastTx broadcasts a transactions either synchronously or
|
||||
// asynchronously based on the context parameters. The result of the broadcast
|
||||
// is parsed into an intermediate structure which is logged if the context has
|
||||
// a logger defined.
|
||||
func (ctx CLIContext) EnsureBroadcastTx(txBytes []byte) error {
|
||||
if ctx.Async {
|
||||
return ctx.ensureBroadcastTxAsync(txBytes)
|
||||
}
|
||||
|
||||
return ctx.ensureBroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
func (ctx CLIContext) ensureBroadcastTxAsync(txBytes []byte) error {
|
||||
res, err := ctx.BroadcastTxAsync(txBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.JSON {
|
||||
type toJSON struct {
|
||||
TxHash string
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resJSON := toJSON{res.Hash.String()}
|
||||
bz, err := ctx.Codec.MarshalJSON(resJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Logger.Write(bz)
|
||||
io.WriteString(ctx.Logger, "\n")
|
||||
}
|
||||
} else {
|
||||
if ctx.Logger != nil {
|
||||
io.WriteString(ctx.Logger, fmt.Sprintf("Async tx sent (tx hash: %s)\n", res.Hash))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error {
|
||||
res, err := ctx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.JSON {
|
||||
// since JSON is intended for automated scripts, always include
|
||||
// response in JSON mode.
|
||||
type toJSON struct {
|
||||
Height int64
|
||||
TxHash string
|
||||
Response abci.ResponseDeliverTx
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resJSON := toJSON{res.Height, res.Hash.String(), res.DeliverTx}
|
||||
bz, err := ctx.Codec.MarshalJSON(resJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Logger.Write(bz)
|
||||
io.WriteString(ctx.Logger, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resStr := fmt.Sprintf("Committed at block %d (tx hash: %s)\n", res.Height, res.Hash.String())
|
||||
|
||||
if ctx.PrintResponse {
|
||||
resStr = fmt.Sprintf("Committed at block %d (tx hash: %s, response: %+v)\n",
|
||||
res.Height, res.Hash.String(), res.DeliverTx,
|
||||
)
|
||||
}
|
||||
|
||||
io.WriteString(ctx.Logger, resStr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// query performs a query from a Tendermint node with the provided store name
|
||||
// and path.
|
||||
func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err error) {
|
||||
|
@ -310,7 +171,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
|
|||
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
|
||||
}
|
||||
|
||||
// Data from trusted node or subspace query doesn't need verification
|
||||
// data from trusted node or subspace query doesn't need verification
|
||||
if ctx.TrustNode || !isQueryStoreWithProof(path) {
|
||||
return resp.Value, nil
|
||||
}
|
||||
|
@ -323,42 +184,52 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
|
|||
return resp.Value, nil
|
||||
}
|
||||
|
||||
// verifyProof perform response proof verification
|
||||
// nolint: unparam
|
||||
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
|
||||
// Verify verifies the consensus proof at given height.
|
||||
func (ctx CLIContext) Verify(height int64) (lite.Commit, error) {
|
||||
check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier)
|
||||
switch {
|
||||
case tmliteErr.IsCommitNotFoundErr(err):
|
||||
return lite.Commit{}, ErrVerifyCommit(height)
|
||||
case err != nil:
|
||||
return lite.Commit{}, err
|
||||
}
|
||||
|
||||
return check, nil
|
||||
}
|
||||
|
||||
// verifyProof perform response proof verification.
|
||||
func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error {
|
||||
if ctx.Verifier == nil {
|
||||
return fmt.Errorf("missing valid verifier to verify data from untrusted node")
|
||||
return fmt.Errorf("missing valid certifier to verify data from distrusted node")
|
||||
}
|
||||
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// AppHash for height H is in header H+1
|
||||
commit, err := tmliteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Verifier)
|
||||
// the AppHash for height H is in header H+1
|
||||
commit, err := ctx.Verify(resp.Height + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var multiStoreProof store.MultiStoreProof
|
||||
cdc := wire.NewCodec()
|
||||
cdc := codec.New()
|
||||
|
||||
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshalBinary rangeProof")
|
||||
}
|
||||
|
||||
// Verify the substore commit hash against trusted appHash
|
||||
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
|
||||
multiStoreProof.StoreInfos, commit.Header.AppHash)
|
||||
// verify the substore commit hash against trusted appHash
|
||||
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(
|
||||
multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in verifying the proof against appHash")
|
||||
}
|
||||
|
||||
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed in the range proof verification")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -370,11 +241,12 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([
|
|||
}
|
||||
|
||||
// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
|
||||
// queryType can be app or store
|
||||
// queryType can be app or store.
|
||||
func isQueryStoreWithProof(path string) bool {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return false
|
||||
}
|
||||
|
||||
paths := strings.SplitN(path[1:], "/", 3)
|
||||
if len(paths) != 3 {
|
||||
return false
|
||||
|
@ -383,5 +255,6 @@ func isQueryStoreWithProof(path string) bool {
|
|||
if store.RequireProof("/" + paths[2]) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
package client
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// nolint
|
||||
const (
|
||||
|
@ -9,6 +15,7 @@ const (
|
|||
// occur between the tx simulation and the actual run.
|
||||
DefaultGasAdjustment = 1.0
|
||||
DefaultGasLimit = 200000
|
||||
GasFlagSimulate = "simulate"
|
||||
|
||||
FlagUseLedger = "ledger"
|
||||
FlagChainID = "chain-id"
|
||||
|
@ -32,17 +39,23 @@ const (
|
|||
|
||||
// LineBreak can be included in a command list to provide a blank line
|
||||
// to help with readability
|
||||
var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}}
|
||||
var (
|
||||
LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}}
|
||||
GasFlagVar = GasSetting{Gas: DefaultGasLimit}
|
||||
)
|
||||
|
||||
// GetCommands adds common flags to query commands
|
||||
func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||
for _, c := range cmds {
|
||||
// TODO: make this default false when we support proofs
|
||||
c.Flags().Bool(FlagTrustNode, true, "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().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block")
|
||||
viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode))
|
||||
viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger))
|
||||
viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID))
|
||||
viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode))
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
@ -50,7 +63,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
|||
// PostCommands adds common flags for commands to post tx
|
||||
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||
for _, c := range cmds {
|
||||
c.Flags().String(FlagFrom, "", "Name 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(FlagSequence, 0, "Sequence number to sign the tx")
|
||||
c.Flags().String(FlagMemo, "", "Memo to send along with transaction")
|
||||
|
@ -58,14 +71,61 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
|||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
||||
c.Flags().Int64(FlagGas, DefaultGasLimit, "gas limit to set per-transaction; set to 0 to calculate required gas automatically")
|
||||
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
|
||||
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
|
||||
c.Flags().Bool(FlagJson, false, "return output in json format")
|
||||
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
|
||||
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses")
|
||||
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
|
||||
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
||||
c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT")
|
||||
// --gas can accept integers and "simulate"
|
||||
c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf(
|
||||
"gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", GasFlagSimulate, DefaultGasLimit))
|
||||
viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode))
|
||||
viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger))
|
||||
viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID))
|
||||
viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode))
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
// Gas flag parsing functions
|
||||
|
||||
// GasSetting encapsulates the possible values passed through the --gas flag.
|
||||
type GasSetting struct {
|
||||
Simulate bool
|
||||
Gas int64
|
||||
}
|
||||
|
||||
// Type returns the flag's value type.
|
||||
func (v *GasSetting) Type() string { return "string" }
|
||||
|
||||
// Set parses and sets the value of the --gas flag.
|
||||
func (v *GasSetting) Set(s string) (err error) {
|
||||
v.Simulate, v.Gas, err = ReadGasFlag(s)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *GasSetting) String() string {
|
||||
if v.Simulate {
|
||||
return GasFlagSimulate
|
||||
}
|
||||
return strconv.FormatInt(v.Gas, 10)
|
||||
}
|
||||
|
||||
// ParseGasFlag parses the value of the --gas flag.
|
||||
func ReadGasFlag(s string) (simulate bool, gas int64, err error) {
|
||||
switch s {
|
||||
case "":
|
||||
gas = DefaultGasLimit
|
||||
case GasFlagSimulate:
|
||||
simulate = true
|
||||
default:
|
||||
gas, err = strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -100,6 +100,19 @@ func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// GetString simply returns the trimmed string output of a given reader.
|
||||
func GetString(prompt string, buf *bufio.Reader) (string, error) {
|
||||
if inputIsTty() && prompt != "" {
|
||||
PrintPrefixed(prompt)
|
||||
}
|
||||
|
||||
out, err := readLineFromBuf(buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(out), nil
|
||||
}
|
||||
|
||||
// inputIsTty returns true iff we have an interactive prompt,
|
||||
// where we can disable echo and request to repeat the password.
|
||||
// If false, we can optimize for piped input from another command
|
||||
|
@ -117,3 +130,8 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) {
|
|||
}
|
||||
return strings.TrimSpace(pass), nil
|
||||
}
|
||||
|
||||
// PrintPrefixed prints a string with > prefixed for use in prompts.
|
||||
func PrintPrefixed(msg string) {
|
||||
fmt.Printf("> %s\n", msg)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ phrase, otherwise, a new key will be generated.`,
|
|||
return cmd
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
// TODO remove the above when addressing #1446
|
||||
func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||
var kb keys.Keybase
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package keys
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
var cdc *wire.Codec
|
||||
var cdc *codec.Codec
|
||||
|
||||
func init() {
|
||||
cdc = wire.NewCodec()
|
||||
wire.RegisterCrypto(cdc)
|
||||
cdc = codec.New()
|
||||
codec.RegisterCrypto(cdc)
|
||||
}
|
||||
|
||||
// marshal keys
|
|
@ -5,13 +5,11 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -19,7 +17,7 @@ const (
|
|||
FlagAddress = "address"
|
||||
// FlagPublicKey represents the user's public key on the command line.
|
||||
FlagPublicKey = "pubkey"
|
||||
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key
|
||||
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
|
||||
FlagBechPrefix = "bech"
|
||||
)
|
||||
|
||||
|
@ -29,39 +27,7 @@ func showKeysCmd() *cobra.Command {
|
|||
Short: "Show key info for the given name",
|
||||
Long: `Return public details of one local key.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
info, err := getKey(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
showAddress := viper.GetBool(FlagAddress)
|
||||
showPublicKey := viper.GetBool(FlagPublicKey)
|
||||
outputSet := cmd.Flag(cli.OutputFlag).Changed
|
||||
|
||||
if showAddress && showPublicKey {
|
||||
return errors.New("cannot use both --address and --pubkey at once")
|
||||
}
|
||||
if outputSet && (showAddress || showPublicKey) {
|
||||
return errors.New("cannot use --output with --address or --pubkey")
|
||||
}
|
||||
|
||||
bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case showAddress:
|
||||
printKeyAddress(info, bechKeyOut)
|
||||
case showPublicKey:
|
||||
printPubKey(info, bechKeyOut)
|
||||
default:
|
||||
printKeyInfo(info, bechKeyOut)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: runShowCmd,
|
||||
}
|
||||
|
||||
cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)")
|
||||
|
@ -71,6 +37,43 @@ func showKeysCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func runShowCmd(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
info, err := GetKeyInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isShowAddr := viper.GetBool(FlagAddress)
|
||||
isShowPubKey := viper.GetBool(FlagPublicKey)
|
||||
isOutputSet := cmd.Flag(cli.OutputFlag).Changed
|
||||
|
||||
if isShowAddr && isShowPubKey {
|
||||
return errors.New("cannot use both --address and --pubkey at once")
|
||||
}
|
||||
|
||||
if isOutputSet && (isShowAddr || isShowPubKey) {
|
||||
return errors.New("cannot use --output with --address or --pubkey")
|
||||
}
|
||||
|
||||
bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isShowAddr:
|
||||
printKeyAddress(info, bechKeyOut)
|
||||
case isShowPubKey:
|
||||
printPubKey(info, bechKeyOut)
|
||||
default:
|
||||
printKeyInfo(info, bechKeyOut)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||
switch bechPrefix {
|
||||
case "acc":
|
||||
|
@ -84,15 +87,6 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
|||
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
|
||||
}
|
||||
|
||||
func getKey(name string) (keys.Info, error) {
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb.Get(name)
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// REST
|
||||
|
||||
|
@ -113,7 +107,7 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
info, err := getKey(name)
|
||||
info, err := GetKeyInfo(name)
|
||||
// TODO: check for the error if key actually does not exist, instead of
|
||||
// assuming this as the reason
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// default: 30 days
|
||||
const defaultValidFor = 30 * 24 * time.Hour
|
||||
|
||||
func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateKey, err error) {
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(defaultValidFor)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to generate serial number: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Gaia Lite"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
hosts := strings.Split(host, ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
certBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("couldn't create certificate: %s", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeCertAndPrivKey(certBytes []byte, priv *ecdsa.PrivateKey) (certFile string, keyFile string, err error) {
|
||||
if priv == nil {
|
||||
err = errors.New("private key is nil")
|
||||
return
|
||||
}
|
||||
certFile, err = writeCertificateFile(certBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
keyFile, err = writeKeyFile(priv)
|
||||
return
|
||||
}
|
||||
|
||||
func writeCertificateFile(certBytes []byte) (filename string, err error) {
|
||||
f, err := ioutil.TempFile("", "cert_")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
filename = f.Name()
|
||||
if err := pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil {
|
||||
return filename, fmt.Errorf("failed to write data to %s: %s", filename, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeKeyFile(priv *ecdsa.PrivateKey) (filename string, err error) {
|
||||
f, err := ioutil.TempFile("", "key_")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
filename = f.Name()
|
||||
block, err := pemBlockForKey(priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := pem.Encode(f, block); err != nil {
|
||||
return filename, fmt.Errorf("failed to write data to %s: %s", filename, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv *ecdsa.PrivateKey) (*pem.Block, error) {
|
||||
b, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to marshal ECDSA private key: %v", err)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil
|
||||
|
||||
}
|
||||
|
||||
func genCertKeyFilesAndReturnFingerprint(sslHosts string) (certFile, keyFile string, fingerprint string, err error) {
|
||||
certBytes, priv, err := generateSelfSignedCert(sslHosts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
certFile, keyFile, err = writeCertAndPrivKey(certBytes, priv)
|
||||
cleanupFunc := func() {
|
||||
os.Remove(certFile)
|
||||
os.Remove(keyFile)
|
||||
}
|
||||
// Either of the files could have been written already,
|
||||
// thus clean up regardless of the error.
|
||||
if err != nil {
|
||||
defer cleanupFunc()
|
||||
return
|
||||
}
|
||||
fingerprint, err = fingerprintForCertificate(certBytes)
|
||||
if err != nil {
|
||||
defer cleanupFunc()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fingerprintForCertificate(certBytes []byte) (string, error) {
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h := sha256.New()
|
||||
h.Write(cert.Raw)
|
||||
fingerprintBytes := h.Sum(nil)
|
||||
var buf bytes.Buffer
|
||||
for i, b := range fingerprintBytes {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(&buf, ":")
|
||||
}
|
||||
fmt.Fprintf(&buf, "%02X", b)
|
||||
}
|
||||
return fmt.Sprintf("SHA256 Fingerprint=%s", buf.String()), nil
|
||||
}
|
||||
|
||||
func fingerprintFromFile(certFile string) (string, error) {
|
||||
f, err := os.Open(certFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
block, _ := pem.Decode(data)
|
||||
if block == nil {
|
||||
return "", fmt.Errorf("couldn't find PEM data in %s", certFile)
|
||||
}
|
||||
return fingerprintForCertificate(block.Bytes)
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateSelfSignedCert(t *testing.T) {
|
||||
host := "127.0.0.1,localhost,::1"
|
||||
certBytes, _, err := generateSelfSignedCert(host)
|
||||
require.Nil(t, err)
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 2, len(cert.IPAddresses))
|
||||
require.Equal(t, 1, len(cert.DNSNames))
|
||||
require.True(t, cert.IsCA)
|
||||
}
|
||||
|
||||
func TestWriteCertAndPrivKey(t *testing.T) {
|
||||
expectedPerm := "-rw-------"
|
||||
derBytes, priv, err := generateSelfSignedCert("localhost")
|
||||
require.Nil(t, err)
|
||||
type args struct {
|
||||
certBytes []byte
|
||||
priv *ecdsa.PrivateKey
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid certificate", args{derBytes, priv}, false},
|
||||
{"garbage", args{[]byte("some garbage"), nil}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotCertFile, gotKeyFile, err := writeCertAndPrivKey(tt.args.certBytes, tt.args.priv)
|
||||
defer os.Remove(gotCertFile)
|
||||
defer os.Remove(gotKeyFile)
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err)
|
||||
return
|
||||
}
|
||||
require.Nil(t, err)
|
||||
info, err := os.Stat(gotCertFile)
|
||||
require.Nil(t, err)
|
||||
require.True(t, info.Mode().IsRegular())
|
||||
require.Equal(t, expectedPerm, info.Mode().String())
|
||||
info, err = os.Stat(gotKeyFile)
|
||||
require.Nil(t, err)
|
||||
require.True(t, info.Mode().IsRegular())
|
||||
require.Equal(t, expectedPerm, info.Mode().String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFingerprintFromFile(t *testing.T) {
|
||||
cert := `-----BEGIN CERTIFICATE-----
|
||||
MIIBbDCCARGgAwIBAgIQSuFKYv/22v+cxtVgMUrQADAKBggqhkjOPQQDAjASMRAw
|
||||
DgYDVQQKEwdBY21lIENvMB4XDTE4MDkyMDIzNDQyNloXDTE5MDkyMDIzNDQyNlow
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDIo
|
||||
ujAesRczcPVAWiLhpeV1B7hS/RI2LJaGj3QjyJ8hiUthJTPIamr8m7LuS/U5fS0o
|
||||
hY297YeTIGo9YkxClICjSTBHMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MA8GA1UdEQQIMAaHBH8AAAEwCgYIKoZI
|
||||
zj0EAwIDSQAwRgIhAKnwbhX9FrGG1otCVLwhClQ3RaLxnNpCgIGTqSimb34cAiEA
|
||||
stMN+IqMCKWlZyGqxGIiyksMLMEU3lRqKNQn2EoAZJY=
|
||||
-----END CERTIFICATE-----`
|
||||
wantFingerprint := `SHA256 Fingerprint=0B:ED:9A:AA:A2:D1:7E:B2:53:56:F6:FC:C0:E6:1A:69:70:21:A2:B0:90:FC:AF:BB:EF:AE:2C:78:52:AB:68:40`
|
||||
certFile, err := ioutil.TempFile("", "test_cert_")
|
||||
require.Nil(t, err)
|
||||
_, err = certFile.Write([]byte(cert))
|
||||
require.Nil(t, err)
|
||||
err = certFile.Close()
|
||||
require.Nil(t, err)
|
||||
defer os.Remove(certFile.Name())
|
||||
fingerprint, err := fingerprintFromFile(certFile.Name())
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, wantFingerprint, fingerprint)
|
||||
|
||||
// test failure
|
||||
emptyFile, err := ioutil.TempFile("", "test_cert_")
|
||||
require.Nil(t, err)
|
||||
err = emptyFile.Close()
|
||||
require.Nil(t, err)
|
||||
defer os.Remove(emptyFile.Name())
|
||||
_, err = fingerprintFromFile(emptyFile.Name())
|
||||
require.NotNil(t, err)
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -22,19 +23,20 @@ import (
|
|||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
tests "github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
version "github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cryptoKeys.BcryptSecurityParameter = 1
|
||||
version.Version = os.Getenv("VERSION")
|
||||
}
|
||||
|
||||
func TestKeys(t *testing.T) {
|
||||
|
@ -61,7 +63,7 @@ func TestKeys(t *testing.T) {
|
|||
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var resp keys.KeyOutput
|
||||
err = wire.Cdc.UnmarshalJSON([]byte(body), &resp)
|
||||
err = codec.Cdc.UnmarshalJSON([]byte(body), &resp)
|
||||
require.Nil(t, err, body)
|
||||
|
||||
addr2Bech32 := resp.Address
|
||||
|
@ -118,6 +120,11 @@ func TestKeys(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
// skip the test if the VERSION environment variable has not been set
|
||||
if version.Version == "" {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
|
||||
defer cleanup()
|
||||
|
||||
|
@ -125,16 +132,16 @@ func TestVersion(t *testing.T) {
|
|||
res, body := Request(t, port, "GET", "/version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+.*`)
|
||||
require.Nil(t, err)
|
||||
match := reg.MatchString(body)
|
||||
require.True(t, match, body)
|
||||
require.True(t, match, body, fmt.Sprintf("%s", body))
|
||||
|
||||
// node info
|
||||
res, body = Request(t, port, "GET", "/node_version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
reg, err = regexp.Compile(`\d+\.\d+\.\d+.*`)
|
||||
require.Nil(t, err)
|
||||
match = reg.MatchString(body)
|
||||
require.True(t, match, body)
|
||||
|
@ -178,10 +185,10 @@ func TestBlock(t *testing.T) {
|
|||
|
||||
// --
|
||||
|
||||
res, body = Request(t, port, "GET", "/blocks/1", nil)
|
||||
res, body = Request(t, port, "GET", "/blocks/2", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = wire.Cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
err = codec.Cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
require.Nil(t, err, "Couldn't parse block")
|
||||
|
||||
require.NotEqual(t, ctypes.ResultBlock{}, resultBlock)
|
||||
|
@ -211,7 +218,7 @@ func TestValidators(t *testing.T) {
|
|||
|
||||
// --
|
||||
|
||||
res, body = Request(t, port, "GET", "/validatorsets/1", nil)
|
||||
res, body = Request(t, port, "GET", "/validatorsets/2", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultVals)
|
||||
|
@ -267,25 +274,33 @@ func TestCoinSend(t *testing.T) {
|
|||
require.Equal(t, int64(1), mycoins.Amount.Int64())
|
||||
|
||||
// test failure with too little gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, 0, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with negative gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with 0 gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with wrong adjustment
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 0.1, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// run simulation and test success with estimated gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?simulate=true")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 0, "?simulate=true")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var responseBody struct {
|
||||
GasEstimate int64 `json:"gas_estimate"`
|
||||
}
|
||||
require.Nil(t, json.Unmarshal([]byte(body), &responseBody))
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, 0, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
}
|
||||
|
||||
func TestIBCTransfer(t *testing.T) {
|
||||
func DisabledTestIBCTransfer(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||
|
@ -322,7 +337,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
|||
acc := getAccount(t, port, addr)
|
||||
|
||||
// generate TX
|
||||
res, body, _ := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?generate_only=true")
|
||||
res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, "?generate_only=true")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var msg auth.StdTx
|
||||
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg))
|
||||
|
@ -478,12 +493,12 @@ func TestValidatorsQuery(t *testing.T) {
|
|||
|
||||
// make sure all the validators were found (order unknown because sorted by operator addr)
|
||||
foundVal := false
|
||||
pkBech := sdk.MustBech32ifyConsPub(pks[0])
|
||||
if validators[0].ConsPubKey == pkBech {
|
||||
|
||||
if validators[0].ConsPubKey == pks[0] {
|
||||
foundVal = true
|
||||
}
|
||||
|
||||
require.True(t, foundVal, "pkBech %v, operator %v", pkBech, validators[0].OperatorAddr)
|
||||
require.True(t, foundVal, "pk %v, operator %v", pks[0], validators[0].OperatorAddr)
|
||||
}
|
||||
|
||||
func TestValidatorQuery(t *testing.T) {
|
||||
|
@ -502,6 +517,7 @@ func TestBonding(t *testing.T) {
|
|||
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||
defer cleanup()
|
||||
|
||||
amt := sdk.NewDec(60)
|
||||
validator1Operator := sdk.ValAddress(pks[0].Address())
|
||||
validator := getValidator(t, port, validator1Operator)
|
||||
|
||||
|
@ -519,18 +535,18 @@ func TestBonding(t *testing.T) {
|
|||
|
||||
// query validator
|
||||
bond := getDelegation(t, port, addr, validator1Operator)
|
||||
require.Equal(t, "60.0000000000", bond.Shares)
|
||||
require.Equal(t, amt, bond.Shares)
|
||||
|
||||
summary := getDelegationSummary(t, port, addr)
|
||||
|
||||
require.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations")
|
||||
require.Equal(t, "60.0000000000", summary.Delegations[0].Shares)
|
||||
require.Equal(t, amt, summary.Delegations[0].Shares)
|
||||
require.Len(t, summary.UnbondingDelegations, 0, "Delegation summary holds all unbonding-delegations")
|
||||
|
||||
bondedValidators := getDelegatorValidators(t, port, addr)
|
||||
require.Len(t, bondedValidators, 1)
|
||||
require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr)
|
||||
require.Equal(t, validator.DelegatorShares.Add(sdk.NewDec(60)).String(), bondedValidators[0].DelegatorShares.String())
|
||||
require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String())
|
||||
|
||||
bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator)
|
||||
require.Equal(t, validator1Operator, bondedValidator.OperatorAddr)
|
||||
|
@ -550,9 +566,8 @@ func TestBonding(t *testing.T) {
|
|||
coins = acc.GetCoins()
|
||||
require.Equal(t, int64(40), coins.AmountOf("steak").Int64())
|
||||
|
||||
unbondings := getUndelegations(t, port, addr, validator1Operator)
|
||||
require.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations")
|
||||
require.Equal(t, "60", unbondings[0].Balance.Amount.String())
|
||||
unbonding := getUndelegation(t, port, addr, validator1Operator)
|
||||
require.Equal(t, "60", unbonding.Balance.Amount.String())
|
||||
|
||||
summary = getDelegationSummary(t, port, addr)
|
||||
|
||||
|
@ -792,7 +807,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
|||
return acc
|
||||
}
|
||||
|
||||
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
|
@ -811,33 +826,35 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
|||
}
|
||||
|
||||
gasStr := ""
|
||||
if gas > 0 {
|
||||
if len(gas) != 0 {
|
||||
gasStr = fmt.Sprintf(`
|
||||
"gas":"%v",
|
||||
"gas":%q,
|
||||
`, gas)
|
||||
}
|
||||
gasAdjustmentStr := ""
|
||||
if gasAdjustment > 0 {
|
||||
gasStr = fmt.Sprintf(`
|
||||
gasAdjustmentStr = fmt.Sprintf(`
|
||||
"gas_adjustment":"%v",
|
||||
`, gasAdjustment)
|
||||
}
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
%v%v
|
||||
"name":"%s",
|
||||
"password":"%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d",
|
||||
"amount":[%s],
|
||||
"chain_id":"%s"
|
||||
}`, gasStr, gasAdjustmentStr, name, password, accnum, sequence, coinbz, chainID))
|
||||
"base_req": {
|
||||
%v%v
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"chain_id": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d"
|
||||
}
|
||||
}`, coinbz, gasStr, gasAdjustmentStr, name, password, chainID, accnum, sequence))
|
||||
|
||||
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr)
|
||||
return
|
||||
}
|
||||
|
||||
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "")
|
||||
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
|
@ -862,18 +879,20 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
|||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name":"%s",
|
||||
"password": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence": "%d",
|
||||
"src_chain_id": "%s",
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": "1"
|
||||
}
|
||||
]
|
||||
}`, name, password, accnum, sequence, chainID, "steak"))
|
||||
],
|
||||
"base_req": {
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"chain_id": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d"
|
||||
}
|
||||
}`, "steak", name, password, chainID, accnum, sequence))
|
||||
|
||||
res, body := Request(t, port, "POST", fmt.Sprintf("/ibc/testchain/%s/send", receiveAddr), jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -887,43 +906,43 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
|||
func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/signing_info/%s", validatorPubKey), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var signingInfo slashing.ValidatorSigningInfo
|
||||
err := cdc.UnmarshalJSON([]byte(body), &signingInfo)
|
||||
require.Nil(t, err)
|
||||
|
||||
return signingInfo
|
||||
}
|
||||
|
||||
// ============= Stake Module ================
|
||||
|
||||
func getDelegation(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) rest.DelegationWithoutRat {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delAddr, valAddr), nil)
|
||||
func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var bond rest.DelegationWithoutRat
|
||||
|
||||
var bond stake.Delegation
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bond)
|
||||
require.Nil(t, err)
|
||||
|
||||
return bond
|
||||
}
|
||||
|
||||
func getUndelegations(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) []stake.UnbondingDelegation {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delAddr, valAddr), nil)
|
||||
func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.UnbondingDelegation {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var unbondings []stake.UnbondingDelegation
|
||||
|
||||
var unbondings stake.UnbondingDelegation
|
||||
err := cdc.UnmarshalJSON([]byte(body), &unbondings)
|
||||
require.Nil(t, err)
|
||||
|
||||
return unbondings
|
||||
}
|
||||
|
||||
func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary {
|
||||
func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) stake.DelegationSummary {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var summary rest.DelegationSummary
|
||||
var summary stake.DelegationSummary
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &summary)
|
||||
require.Nil(t, err)
|
||||
|
@ -950,11 +969,11 @@ func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, quer
|
|||
return txs
|
||||
}
|
||||
|
||||
func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.BechValidator {
|
||||
func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators", delegatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var bondedValidators []stake.BechValidator
|
||||
var bondedValidators []stake.Validator
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bondedValidators)
|
||||
require.Nil(t, err)
|
||||
|
@ -962,11 +981,11 @@ func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddr
|
|||
return bondedValidators
|
||||
}
|
||||
|
||||
func getDelegatorValidator(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) stake.BechValidator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delAddr, valAddr), nil)
|
||||
func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var bondedValidator stake.BechValidator
|
||||
var bondedValidator stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bondedValidator)
|
||||
require.Nil(t, err)
|
||||
|
||||
|
@ -982,11 +1001,6 @@ func doDelegate(t *testing.T, port, seed, name, password string,
|
|||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": "%d",
|
||||
"sequence": "%d",
|
||||
"chain_id": "%s",
|
||||
"delegations": [
|
||||
{
|
||||
"delegator_addr": "%s",
|
||||
|
@ -997,8 +1011,15 @@ func doDelegate(t *testing.T, port, seed, name, password string,
|
|||
"begin_unbondings": [],
|
||||
"complete_unbondings": [],
|
||||
"begin_redelegates": [],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delAddr, valAddr, "steak", amount))
|
||||
"complete_redelegates": [],
|
||||
"base_req": {
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"chain_id": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d"
|
||||
}
|
||||
}`, delAddr, valAddr, "steak", amount, name, password, chainID, accnum, sequence))
|
||||
|
||||
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -1019,11 +1040,6 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string,
|
|||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": "%d",
|
||||
"sequence": "%d",
|
||||
"chain_id": "%s",
|
||||
"delegations": [],
|
||||
"begin_unbondings": [
|
||||
{
|
||||
|
@ -1034,8 +1050,15 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string,
|
|||
],
|
||||
"complete_unbondings": [],
|
||||
"begin_redelegates": [],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delAddr, valAddr, amount))
|
||||
"complete_redelegates": [],
|
||||
"base_req": {
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"chain_id": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d"
|
||||
}
|
||||
}`, delAddr, valAddr, amount, name, password, chainID, accnum, sequence))
|
||||
|
||||
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -1057,11 +1080,6 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string,
|
|||
chainID := viper.GetString(client.FlagChainID)
|
||||
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": "%d",
|
||||
"sequence": "%d",
|
||||
"chain_id": "%s",
|
||||
"delegations": [],
|
||||
"begin_unbondings": [],
|
||||
"complete_unbondings": [],
|
||||
|
@ -1073,8 +1091,15 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string,
|
|||
"shares": "30"
|
||||
}
|
||||
],
|
||||
"complete_redelegates": []
|
||||
}`, name, password, accnum, sequence, chainID, delAddr, valSrcAddr, valDstAddr))
|
||||
"complete_redelegates": [],
|
||||
"base_req": {
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"chain_id": "%s",
|
||||
"account_number":"%d",
|
||||
"sequence":"%d"
|
||||
}
|
||||
}`, delAddr, valSrcAddr, valDstAddr, name, password, chainID, accnum, sequence))
|
||||
|
||||
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -1086,21 +1111,25 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string,
|
|||
return results[0]
|
||||
}
|
||||
|
||||
func getValidators(t *testing.T, port string) []stake.BechValidator {
|
||||
func getValidators(t *testing.T, port string) []stake.Validator {
|
||||
res, body := Request(t, port, "GET", "/stake/validators", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validators []stake.BechValidator
|
||||
|
||||
var validators []stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validators)
|
||||
require.Nil(t, err)
|
||||
|
||||
return validators
|
||||
}
|
||||
|
||||
func getValidator(t *testing.T, port string, valAddr sdk.ValAddress) stake.BechValidator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", valAddr.String()), nil)
|
||||
func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validator stake.BechValidator
|
||||
|
||||
var validator stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validator)
|
||||
require.Nil(t, err)
|
||||
|
||||
return validator
|
||||
}
|
||||
|
||||
|
@ -1276,7 +1305,6 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac
|
|||
}
|
||||
}`, proposerAddr, name, password, chainID, accnum, sequence))
|
||||
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr)
|
||||
fmt.Println(res)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results ctypes.ResultBroadcastTxCommit
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
|
@ -9,11 +11,10 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
||||
gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
|
||||
ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest"
|
||||
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
|
||||
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -24,35 +25,84 @@ import (
|
|||
tmserver "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
)
|
||||
|
||||
const (
|
||||
flagListenAddr = "laddr"
|
||||
flagCORS = "cors"
|
||||
flagMaxOpenConnections = "max-open"
|
||||
flagInsecure = "insecure"
|
||||
flagSSLHosts = "ssl-hosts"
|
||||
flagSSLCertFile = "ssl-certfile"
|
||||
flagSSLKeyFile = "ssl-keyfile"
|
||||
)
|
||||
|
||||
// ServeCommand will generate a long-running rest server
|
||||
// (aka Light Client Daemon) that exposes functionality similar
|
||||
// to the cli, but over rest
|
||||
func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
||||
flagListenAddr := "laddr"
|
||||
flagCORS := "cors"
|
||||
flagMaxOpenConnections := "max-open"
|
||||
func ServeCommand(cdc *codec.Codec) *cobra.Command {
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rest-server",
|
||||
Short: "Start LCD (light-client daemon), a local REST server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
listenAddr := viper.GetString(flagListenAddr)
|
||||
handler := createHandler(cdc)
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
|
||||
maxOpen := viper.GetInt(flagMaxOpenConnections)
|
||||
sslHosts := viper.GetString(flagSSLHosts)
|
||||
certFile := viper.GetString(flagSSLCertFile)
|
||||
keyFile := viper.GetString(flagSSLKeyFile)
|
||||
cleanupFunc := func() {}
|
||||
|
||||
listener, err := tmserver.StartHTTPServer(
|
||||
listenAddr, handler, logger,
|
||||
tmserver.Config{MaxOpenConnections: maxOpen},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
var listener net.Listener
|
||||
var fingerprint string
|
||||
if viper.GetBool(flagInsecure) {
|
||||
listener, err = tmserver.StartHTTPServer(
|
||||
listenAddr, handler, logger,
|
||||
tmserver.Config{MaxOpenConnections: maxOpen},
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if certFile != "" {
|
||||
// validateCertKeyFiles() is needed to work around tendermint/tendermint#2460
|
||||
err = validateCertKeyFiles(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// cert/key pair is provided, read the fingerprint
|
||||
fingerprint, err = fingerprintFromFile(certFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// if certificate is not supplied, generate a self-signed one
|
||||
certFile, keyFile, fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cleanupFunc = func() {
|
||||
os.Remove(certFile)
|
||||
os.Remove(keyFile)
|
||||
}
|
||||
defer cleanupFunc()
|
||||
}
|
||||
listener, err = tmserver.StartHTTPAndTLSServer(
|
||||
listenAddr, handler,
|
||||
certFile, keyFile,
|
||||
logger,
|
||||
tmserver.Config{MaxOpenConnections: maxOpen},
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
logger.Info(fingerprint)
|
||||
}
|
||||
|
||||
logger.Info("REST server started")
|
||||
|
||||
// wait forever and cleanup
|
||||
cmn.TrapSignal(func() {
|
||||
defer cleanupFunc()
|
||||
err := listener.Close()
|
||||
logger.Error("error closing listener", "err", err)
|
||||
})
|
||||
|
@ -62,16 +112,23 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on")
|
||||
cmd.Flags().Bool(flagInsecure, false, "Do not set up SSL/TLS layer")
|
||||
cmd.Flags().String(flagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for")
|
||||
cmd.Flags().String(flagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.")
|
||||
cmd.Flags().String(flagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.")
|
||||
cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)")
|
||||
cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to")
|
||||
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
|
||||
cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to")
|
||||
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node")
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func createHandler(cdc *wire.Codec) http.Handler {
|
||||
func createHandler(cdc *codec.Codec) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
|
||||
kb, err := keys.GetKeyBase() //XXX
|
||||
|
@ -90,10 +147,22 @@ func createHandler(cdc *wire.Codec) http.Handler {
|
|||
tx.RegisterRoutes(cliCtx, r, cdc)
|
||||
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
|
||||
bank.RegisterRoutes(cliCtx, r, cdc, kb)
|
||||
ibc.RegisterRoutes(cliCtx, r, cdc, kb)
|
||||
stake.RegisterRoutes(cliCtx, r, cdc, kb)
|
||||
slashing.RegisterRoutes(cliCtx, r, cdc, kb)
|
||||
gov.RegisterRoutes(cliCtx, r, cdc)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func validateCertKeyFiles(certFile, keyFile string) error {
|
||||
if keyFile == "" {
|
||||
return errors.New("a key file is required")
|
||||
}
|
||||
if _, err := os.Stat(certFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(keyFile); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
@ -177,7 +177,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
|||
genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewDec(100))
|
||||
}
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
appState, err := codec.MarshalJSONIndent(cdc, genesisState)
|
||||
require.NoError(t, err)
|
||||
genDoc.AppState = appState
|
||||
|
||||
|
@ -187,6 +187,10 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
|||
// XXX: Need to set this so LCD knows the tendermint node address!
|
||||
viper.Set(client.FlagNode, config.RPC.ListenAddress)
|
||||
viper.Set(client.FlagChainID, genDoc.ChainID)
|
||||
viper.Set(client.FlagTrustNode, false)
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
require.NoError(t, err)
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
|
||||
node, err := startTM(config, logger, genDoc, privVal, app)
|
||||
require.NoError(t, err)
|
||||
|
@ -251,7 +255,7 @@ func startTM(
|
|||
// startLCD starts the LCD.
|
||||
//
|
||||
// NOTE: This causes the thread to block.
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) {
|
||||
return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{})
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
)
|
||||
|
||||
//BlockCommand returns the verified block data for a given heights
|
||||
|
@ -21,8 +23,10 @@ func BlockCommand() *cobra.Command {
|
|||
RunE: printBlock,
|
||||
}
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||
// TODO: change this to false when we can
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -41,8 +45,25 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if !cliCtx.TrustNode {
|
||||
check, err := cliCtx.Certify(res.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = tmliteProxy.ValidateBlockMeta(res.BlockMeta, check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = tmliteProxy.ValidateBlock(res.Block, check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move maarshalling into cmd/rest functions
|
||||
// output, err := tmwire.MarshalJSON(res)
|
||||
// output, err := tmcodec.MarshalJSON(res)
|
||||
output, err := cdc.MarshalJSON(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -40,6 +41,9 @@ func initClientCommand() *cobra.Command {
|
|||
cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity")
|
||||
cmd.Flags().String(flagCommit, "", "File with trusted and signed header")
|
||||
cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)")
|
||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func statusCommand() *cobra.Command {
|
||||
|
@ -20,6 +21,7 @@ func statusCommand() *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -12,6 +13,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// TODO these next two functions feel kinda hacky based on their placement
|
||||
|
@ -19,14 +21,17 @@ import (
|
|||
//ValidatorCommand returns the validator set for a given height
|
||||
func ValidatorCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validator-set [height]",
|
||||
Use: "tendermint-validator-set [height]",
|
||||
Short: "Get the full tendermint validator set at given height",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: printValidators,
|
||||
}
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||
// TODO: change this to false when we can
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
|
||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -70,6 +75,17 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if !cliCtx.TrustNode {
|
||||
check, err := cliCtx.Certify(validatorsRes.BlockHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(check.ValidatorsHash(), tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) {
|
||||
return nil, fmt.Errorf("got invalid validatorset")
|
||||
}
|
||||
}
|
||||
|
||||
outputValidatorsRes := ResultValidatorsOutput{
|
||||
BlockHeight: validatorsRes.BlockHeight,
|
||||
Validators: make([]ValidatorOutput, len(validatorsRes.Validators)),
|
||||
|
|
|
@ -25,7 +25,7 @@ func BroadcastTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx([]byte(m.TxBytes))
|
||||
res, err := cliCtx.BroadcastTxAndAwaitCommit([]byte(m.TxBytes))
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -4,25 +4,23 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// QueryTxCmd implements the default command for a tx query.
|
||||
func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
func QueryTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "tx [hash]",
|
||||
Short: "Matches this txhash over all committed blocks",
|
||||
|
@ -30,11 +28,10 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// find the key to look up the account
|
||||
hashHexStr := args[0]
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
|
||||
output, err := queryTx(cdc, cliCtx, hashHexStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -45,13 +42,15 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||
|
||||
// TODO: change this to false when we can
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
|
||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) {
|
||||
func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([]byte, error) {
|
||||
hash, err := hex.DecodeString(hashHexStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -62,21 +61,41 @@ func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trus
|
|||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.Tx(hash, !trustNode)
|
||||
res, err := node.Tx(hash, !cliCtx.TrustNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !cliCtx.TrustNode {
|
||||
err := ValidateTxResult(cliCtx, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
info, err := formatTxResult(cdc, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return wire.MarshalJSONIndent(cdc, info)
|
||||
return codec.MarshalJSONIndent(cdc, info)
|
||||
}
|
||||
|
||||
func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (Info, error) {
|
||||
// TODO: verify the proof if requested
|
||||
// ValidateTxResult performs transaction verification
|
||||
func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error {
|
||||
check, err := cliCtx.Certify(res.Height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = res.Proof.Validate(check.Header.DataHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (Info, error) {
|
||||
tx, err := parseTx(cdc, res.Tx)
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
|
@ -98,7 +117,7 @@ type Info struct {
|
|||
Result abci.ResponseDeliverTx `json:"result"`
|
||||
}
|
||||
|
||||
func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
|
||||
func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) {
|
||||
var tx auth.StdTx
|
||||
|
||||
err := cdc.UnmarshalBinary(txBytes, &tx)
|
||||
|
@ -112,17 +131,12 @@ func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
|
|||
// REST
|
||||
|
||||
// transaction query REST handler
|
||||
func QueryTxRequestHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
hashHexStr := vars["hash"]
|
||||
trustNode, err := strconv.ParseBool(r.FormValue("trust_node"))
|
||||
// trustNode defaults to true
|
||||
if err != nil {
|
||||
trustNode = true
|
||||
}
|
||||
|
||||
output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
|
||||
output, err := queryTx(cdc, cliCtx, hashHexStr)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -5,11 +5,11 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
// AddCommands adds a number of tx-query related subcommands
|
||||
func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
|
||||
func AddCommands(cmd *cobra.Command, cdc *codec.Codec) {
|
||||
cmd.AddCommand(
|
||||
SearchTxCmd(cdc),
|
||||
QueryTxCmd(cdc),
|
||||
|
@ -17,7 +17,7 @@ func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
|
|||
}
|
||||
|
||||
// register REST routes
|
||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) {
|
||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) {
|
||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
|
||||
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
|
||||
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -24,7 +24,7 @@ const (
|
|||
)
|
||||
|
||||
// default client command to search through tagged transactions
|
||||
func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
func SearchTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "txs",
|
||||
Short: "Search for all transactions that match the given tags.",
|
||||
|
@ -62,15 +62,17 @@ $ gaiacli tendermint txs --tag test1,test2 --any
|
|||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
|
||||
|
||||
// TODO: change this to false once proofs built in
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode))
|
||||
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
|
||||
viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID))
|
||||
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
|
||||
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
|
||||
cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match")
|
||||
cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Info, error) {
|
||||
func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]Info, error) {
|
||||
if len(tags) == 0 {
|
||||
return nil, errors.New("must declare at least one tag to search")
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Inf
|
|||
return nil, err
|
||||
}
|
||||
|
||||
prove := !viper.GetBool(client.FlagTrustNode)
|
||||
prove := !cliCtx.TrustNode
|
||||
|
||||
// TODO: take these as args
|
||||
page := 0
|
||||
|
@ -94,6 +96,15 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Inf
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if prove {
|
||||
for _, tx := range res.Txs {
|
||||
err := ValidateTxResult(cliCtx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info, err := FormatTxResults(cdc, res.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -103,7 +114,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Inf
|
|||
}
|
||||
|
||||
// parse the indexed txs into an array of Info
|
||||
func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) {
|
||||
func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) {
|
||||
var err error
|
||||
out := make([]Info, len(res))
|
||||
for i := range res {
|
||||
|
@ -119,7 +130,7 @@ func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) {
|
|||
// REST
|
||||
|
||||
// Search Tx REST Handler
|
||||
func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc {
|
||||
func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
tag := r.FormValue("tag")
|
||||
if tag == "" {
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
package tx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
keybase "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
keys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
)
|
||||
|
||||
// REST request body for signed txs
|
||||
// TODO does this need to be exposed?
|
||||
type SignTxBody struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
TxBytes string `json:"tx"`
|
||||
}
|
||||
|
||||
// sign transaction REST Handler
|
||||
func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m SignTxBody
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&m)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
kb, err = keybase.GetKeyBase()
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//TODO check if account exists
|
||||
sig, _, err := kb.Sign(m.Name, m.Password, []byte(m.TxBytes))
|
||||
if err != nil {
|
||||
w.WriteHeader(403)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(sig)
|
||||
}
|
|
@ -2,10 +2,15 @@ package utils
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||
|
@ -16,6 +21,9 @@ const (
|
|||
queryArgGenerateOnly = "generate_only"
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
// Basic HTTP utilities
|
||||
|
||||
// WriteErrorResponse prepares and writes a HTTP error
|
||||
// given a status code and an error message.
|
||||
func WriteErrorResponse(w http.ResponseWriter, status int, msg string) {
|
||||
|
@ -30,24 +38,45 @@ func WriteSimulationResponse(w http.ResponseWriter, gas int64) {
|
|||
w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas)))
|
||||
}
|
||||
|
||||
// HasDryRunArg returns true if the request's URL query contains
|
||||
// the dry run argument and its value is set to "true".
|
||||
func HasDryRunArg(r *http.Request) bool { return urlQueryHasArg(r.URL, queryArgDryRun) }
|
||||
// HasDryRunArg returns true if the request's URL query contains the dry run
|
||||
// argument and its value is set to "true".
|
||||
func HasDryRunArg(r *http.Request) bool {
|
||||
return urlQueryHasArg(r.URL, queryArgDryRun)
|
||||
}
|
||||
|
||||
// HasGenerateOnlyArg returns whether a URL's query "generate-only" parameter is set to "true".
|
||||
func HasGenerateOnlyArg(r *http.Request) bool { return urlQueryHasArg(r.URL, queryArgGenerateOnly) }
|
||||
// HasGenerateOnlyArg returns whether a URL's query "generate-only" parameter
|
||||
// is set to "true".
|
||||
func HasGenerateOnlyArg(r *http.Request) bool {
|
||||
return urlQueryHasArg(r.URL, queryArgGenerateOnly)
|
||||
}
|
||||
|
||||
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a default
|
||||
// value if the string is empty. Write
|
||||
// ParseInt64OrReturnBadRequest converts s to a int64 value.
|
||||
func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok bool) {
|
||||
var err error
|
||||
|
||||
n, err = strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("'%s' is not a valid int64", s)
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return n, false
|
||||
}
|
||||
|
||||
return n, true
|
||||
}
|
||||
|
||||
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a
|
||||
// default value, defaultIfEmpty, if the string is empty.
|
||||
func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) {
|
||||
if len(s) == 0 {
|
||||
return defaultIfEmpty, true
|
||||
}
|
||||
|
||||
n, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return n, false
|
||||
}
|
||||
|
||||
return n, true
|
||||
}
|
||||
|
||||
|
@ -58,13 +87,164 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, txBldr authtxb.TxBuilder,
|
|||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := txBldr.Codec.MarshalJSON(auth.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo))
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
return
|
||||
}
|
||||
|
||||
func urlQueryHasArg(url *url.URL, arg string) bool { return url.Query().Get(arg) == "true" }
|
||||
|
||||
//----------------------------------------
|
||||
// Building / Sending utilities
|
||||
|
||||
// BaseReq defines a structure that can be embedded in other request structures
|
||||
// that all share common "base" fields.
|
||||
type BaseReq struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas string `json:"gas"`
|
||||
GasAdjustment string `json:"gas_adjustment"`
|
||||
}
|
||||
|
||||
// Sanitize performs basic sanitization on a BaseReq object.
|
||||
func (br BaseReq) Sanitize() BaseReq {
|
||||
return BaseReq{
|
||||
Name: strings.TrimSpace(br.Name),
|
||||
Password: strings.TrimSpace(br.Password),
|
||||
ChainID: strings.TrimSpace(br.ChainID),
|
||||
Gas: strings.TrimSpace(br.Gas),
|
||||
GasAdjustment: strings.TrimSpace(br.GasAdjustment),
|
||||
AccountNumber: br.AccountNumber,
|
||||
Sequence: br.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ReadRESTReq is a simple convenience wrapper that reads the body and
|
||||
unmarshals to the req interface.
|
||||
|
||||
Usage:
|
||||
type SomeReq struct {
|
||||
BaseReq `json:"base_req"`
|
||||
CustomField string `json:"custom_field"`
|
||||
}
|
||||
|
||||
req := new(SomeReq)
|
||||
err := ReadRESTReq(w, r, cdc, req)
|
||||
*/
|
||||
func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalJSON(body, req)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation of a BaseReq. If custom validation
|
||||
// logic is needed, the implementing request handler should perform those
|
||||
// checks manually.
|
||||
func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool {
|
||||
switch {
|
||||
case len(br.Name) == 0:
|
||||
WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified")
|
||||
return false
|
||||
|
||||
case len(br.Password) == 0:
|
||||
WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified")
|
||||
return false
|
||||
|
||||
case len(br.ChainID) == 0:
|
||||
WriteErrorResponse(w, http.StatusUnauthorized, "chainID required but not specified")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CompleteAndBroadcastTxREST implements a utility function that facilitates
|
||||
// sending a series of messages in a signed transaction given a TxBuilder and a
|
||||
// QueryContext. It ensures that the account exists, has a proper number and
|
||||
// sequence set. In addition, it builds and signs a transaction with the
|
||||
// supplied messages. Finally, it broadcasts the signed transaction to a node.
|
||||
//
|
||||
// NOTE: Also see CompleteAndBroadcastTxCli.
|
||||
// NOTE: Also see x/stake/client/rest/tx.go delegationsRequestHandlerFn.
|
||||
func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq BaseReq, msgs []sdk.Msg, cdc *codec.Codec) {
|
||||
simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
adjustment, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
txBldr := authtxb.TxBuilder{
|
||||
Codec: cdc,
|
||||
Gas: gas,
|
||||
GasAdjustment: adjustment,
|
||||
SimulateGas: simulateGas,
|
||||
ChainID: baseReq.ChainID,
|
||||
AccountNumber: baseReq.AccountNumber,
|
||||
Sequence: baseReq.Sequence,
|
||||
}
|
||||
|
||||
if HasDryRunArg(r) || txBldr.SimulateGas {
|
||||
newBldr, err := EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, msgs)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if HasDryRunArg(r) {
|
||||
WriteSimulationResponse(w, newBldr.Gas)
|
||||
return
|
||||
}
|
||||
|
||||
txBldr = newBldr
|
||||
}
|
||||
|
||||
if HasGenerateOnlyArg(r) {
|
||||
WriteGenerateStdTxResponse(w, txBldr, msgs)
|
||||
return
|
||||
}
|
||||
|
||||
txBytes, err := txBldr.BuildAndSign(baseReq.Name, baseReq.Password, msgs)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := cliCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := codec.MarshalJSONIndent(cdc, res)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
|
|
|
@ -8,25 +8,33 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
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"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// SendTx implements a auxiliary handler that facilitates sending a series of
|
||||
// messages in a signed transaction given a TxBuilder and a QueryContext. It
|
||||
// ensures that the account exists, has a proper number and sequence set. In
|
||||
// addition, it builds and signs a transaction with the supplied messages.
|
||||
// Finally, it broadcasts the signed transaction to a node.
|
||||
func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
|
||||
txBldr, err := prepareTxContext(txBldr, cliCtx)
|
||||
// CompleteAndBroadcastTxCli implements a utility function that
|
||||
// facilitates sending a series of messages in a signed
|
||||
// transaction given a TxBuilder and a QueryContext. It ensures
|
||||
// that the account exists, has a proper number and sequence
|
||||
// set. In addition, it builds and signs a transaction with the
|
||||
// supplied messages. Finally, it broadcasts the signed
|
||||
// transaction to a node.
|
||||
// NOTE: Also see CompleteAndBroadcastTxREST.
|
||||
func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
|
||||
txBldr, err := prepareTxBuilder(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
autogas := cliCtx.DryRun || (cliCtx.Gas == 0)
|
||||
if autogas {
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
|
||||
name, err := cliCtx.GetFromName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if txBldr.SimulateGas || cliCtx.DryRun {
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -36,34 +44,25 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg)
|
|||
return nil
|
||||
}
|
||||
|
||||
passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
|
||||
passphrase, err := keys.GetPassphrase(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build and sign the transaction
|
||||
txBytes, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
|
||||
txBytes, err := txBldr.BuildAndSign(name, passphrase, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// broadcast to a Tendermint node
|
||||
return cliCtx.EnsureBroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
|
||||
func SimulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg, gas int64) (estimated, adjusted int64, err error) {
|
||||
txBytes, err := txBldr.WithGas(gas).BuildWithPubKey(name, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
|
||||
return
|
||||
_, err = cliCtx.BroadcastTx(txBytes)
|
||||
return err
|
||||
}
|
||||
|
||||
// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
|
||||
// transaction and set the transaction's respective value accordingly.
|
||||
func EnrichCtxWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) {
|
||||
_, adjusted, err := SimulateMsgs(txBldr, cliCtx, name, msgs, 0)
|
||||
_, adjusted, err := simulateMsgs(txBldr, cliCtx, name, msgs)
|
||||
if err != nil {
|
||||
return txBldr, err
|
||||
}
|
||||
|
@ -143,6 +142,17 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
|
|||
return txBldr.SignStdTx(name, passphrase, stdTx, appendSig)
|
||||
}
|
||||
|
||||
// nolint
|
||||
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
|
||||
func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted int64, err error) {
|
||||
txBytes, err := txBldr.BuildWithPubKey(name, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, txBldr.GasAdjustment)
|
||||
return
|
||||
}
|
||||
|
||||
func adjustGasEstimate(estimate int64, adjustment float64) int64 {
|
||||
return int64(adjustment * float64(estimate))
|
||||
}
|
||||
|
@ -155,7 +165,7 @@ func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
|
|||
return simulationResult.GasUsed, nil
|
||||
}
|
||||
|
||||
func prepareTxContext(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (authtxb.TxBuilder, error) {
|
||||
func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (authtxb.TxBuilder, error) {
|
||||
if err := cliCtx.EnsureAccountExists(); err != nil {
|
||||
return txBldr, err
|
||||
}
|
||||
|
@ -190,12 +200,18 @@ func prepareTxContext(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth
|
|||
// buildUnsignedStdTx builds a StdTx as per the parameters passed in the
|
||||
// contexts. Gas is automatically estimated if gas wanted is set to 0.
|
||||
func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
|
||||
txBldr, err = prepareTxContext(txBldr, cliCtx)
|
||||
txBldr, err = prepareTxBuilder(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if txBldr.Gas == 0 {
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
if txBldr.SimulateGas {
|
||||
var name string
|
||||
name, err = cliCtx.GetFromName()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -12,12 +12,11 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
@ -36,13 +35,13 @@ var (
|
|||
// Extended ABCI application
|
||||
type GaiaApp struct {
|
||||
*bam.BaseApp
|
||||
cdc *wire.Codec
|
||||
cdc *codec.Codec
|
||||
|
||||
// keys to access the substores
|
||||
keyMain *sdk.KVStoreKey
|
||||
keyAccount *sdk.KVStoreKey
|
||||
keyIBC *sdk.KVStoreKey
|
||||
keyStake *sdk.KVStoreKey
|
||||
tkeyStake *sdk.TransientStoreKey
|
||||
keySlashing *sdk.KVStoreKey
|
||||
keyGov *sdk.KVStoreKey
|
||||
keyFeeCollection *sdk.KVStoreKey
|
||||
|
@ -53,7 +52,6 @@ type GaiaApp struct {
|
|||
accountMapper auth.AccountMapper
|
||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||
bankKeeper bank.Keeper
|
||||
ibcMapper ibc.Mapper
|
||||
stakeKeeper stake.Keeper
|
||||
slashingKeeper slashing.Keeper
|
||||
govKeeper gov.Keeper
|
||||
|
@ -72,8 +70,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
|||
cdc: cdc,
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||
keyStake: sdk.NewKVStoreKey("stake"),
|
||||
tkeyStake: sdk.NewTransientStoreKey("transient_stake"),
|
||||
keySlashing: sdk.NewKVStoreKey("slashing"),
|
||||
keyGov: sdk.NewKVStoreKey("gov"),
|
||||
keyFeeCollection: sdk.NewKVStoreKey("fee"),
|
||||
|
@ -89,33 +87,33 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
|||
)
|
||||
|
||||
// add handlers
|
||||
app.bankKeeper = bank.NewKeeper(app.accountMapper)
|
||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
|
||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||
app.stakeKeeper = app.stakeKeeper.WithValidatorHooks(app.slashingKeeper.ValidatorHooks())
|
||||
app.stakeKeeper = app.stakeKeeper.WithHooks(app.slashingKeeper.Hooks())
|
||||
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
|
||||
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
|
||||
|
||||
// register message routes
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
|
||||
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
|
||||
AddRoute("gov", gov.NewHandler(app.govKeeper))
|
||||
|
||||
app.QueryRouter().
|
||||
AddRoute("gov", gov.NewQuerier(app.govKeeper))
|
||||
AddRoute("gov", gov.NewQuerier(app.govKeeper)).
|
||||
AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetInitChainer(app.initChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetEndBlocker(app.EndBlocker)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
|
||||
app.MountStore(app.tkeyParams, sdk.StoreTypeTransient)
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake,
|
||||
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
|
||||
app.MountStoresTransient(app.tkeyParams, app.tkeyStake)
|
||||
err := app.LoadLatestVersion(app.keyMain)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
|
@ -125,16 +123,15 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
|||
}
|
||||
|
||||
// custom tx codec
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
ibc.RegisterWire(cdc)
|
||||
bank.RegisterWire(cdc)
|
||||
stake.RegisterWire(cdc)
|
||||
slashing.RegisterWire(cdc)
|
||||
gov.RegisterWire(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
func MakeCodec() *codec.Codec {
|
||||
var cdc = codec.New()
|
||||
bank.RegisterCodec(cdc)
|
||||
stake.RegisterCodec(cdc)
|
||||
slashing.RegisterCodec(cdc)
|
||||
gov.RegisterCodec(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
return cdc
|
||||
}
|
||||
|
||||
|
@ -219,7 +216,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
|
|||
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
|
||||
GovData: gov.WriteGenesis(ctx, app.govKeeper),
|
||||
}
|
||||
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
|
||||
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -25,7 +25,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
|||
StakeData: stake.DefaultGenesisState(),
|
||||
}
|
||||
|
||||
stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState)
|
||||
stateBytes, err := codec.MarshalJSONIndent(gapp.cdc, genesisState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// This will fail half the time with the second output being 173
|
||||
// This is due to secp256k1 signatures not being constant size.
|
||||
// This will be resolved when updating to tendermint v0.24.0
|
||||
// nolint: vet
|
||||
func ExampleTxSendSize() {
|
||||
cdc := app.MakeCodec()
|
||||
priv1 := secp256k1.GenPrivKeySecp256k1([]byte{0})
|
||||
addr1 := sdk.AccAddress(priv1.PubKey().Address())
|
||||
priv2 := secp256k1.GenPrivKeySecp256k1([]byte{1})
|
||||
addr2 := sdk.AccAddress(priv2.PubKey().Address())
|
||||
coins := []sdk.Coin{sdk.NewCoin("denom", sdk.NewInt(10))}
|
||||
msg1 := bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
sig, _ := priv1.Sign(msg1.GetSignBytes())
|
||||
sigs := []auth.StdSignature{auth.StdSignature{nil, sig, 0, 0}}
|
||||
tx := auth.NewStdTx([]sdk.Msg{msg1}, auth.NewStdFee(0, coins...), sigs, "")
|
||||
fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1})))
|
||||
fmt.Println(len(cdc.MustMarshalBinaryBare(tx)))
|
||||
// output: 80
|
||||
// 173
|
||||
}
|
|
@ -6,13 +6,14 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
|
@ -91,7 +92,7 @@ type GaiaGenTx struct {
|
|||
|
||||
// GaiaAppGenTx generates a Gaia genesis transaction.
|
||||
func GaiaAppGenTx(
|
||||
cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx,
|
||||
cdc *codec.Codec, pk crypto.PubKey, genTxConfig config.GenTx,
|
||||
) (appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
|
||||
if genTxConfig.Name == "" {
|
||||
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
|
||||
|
@ -135,7 +136,7 @@ func GaiaAppGenTx(
|
|||
}
|
||||
|
||||
// Generate a gaia genesis transaction without flags
|
||||
func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) (
|
||||
func GaiaAppGenTxNF(cdc *codec.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) (
|
||||
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
|
||||
|
||||
var bz []byte
|
||||
|
@ -144,7 +145,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name
|
|||
Address: addr,
|
||||
PubKey: sdk.MustBech32ifyConsPub(pk),
|
||||
}
|
||||
bz, err = wire.MarshalJSONIndent(cdc, gaiaGenTx)
|
||||
bz, err = codec.MarshalJSONIndent(cdc, gaiaGenTx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name
|
|||
|
||||
// Create the core parameters for genesis initialization for gaia
|
||||
// note that the pubkey input is this machines pubkey
|
||||
func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
|
||||
func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
|
||||
|
||||
if len(appGenTxs) == 0 {
|
||||
err = errors.New("must provide at least genesis transaction")
|
||||
|
@ -233,7 +234,7 @@ func genesisAccountFromGenTx(genTx GaiaGenTx) GenesisAccount {
|
|||
}
|
||||
|
||||
// GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants
|
||||
// TODO: No validators are both bonded and revoked (#2088)
|
||||
// TODO: No validators are both bonded and jailed (#2088)
|
||||
// TODO: Error if there is a duplicate validator (#1708)
|
||||
// TODO: Ensure all state machine parameters are in genesis (#1704)
|
||||
func GaiaValidateGenesisState(genesisState GenesisState) (err error) {
|
||||
|
@ -241,6 +242,26 @@ func GaiaValidateGenesisState(genesisState GenesisState) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = validateGenesisStateValidators(genesisState.StakeData.Validators)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateGenesisStateValidators(validators []stakeTypes.Validator) (err error) {
|
||||
addrMap := make(map[string]bool, len(validators))
|
||||
for i := 0; i < len(validators); i++ {
|
||||
val := validators[i]
|
||||
strKey := string(val.ConsPubKey.Bytes())
|
||||
if _, ok := addrMap[strKey]; ok {
|
||||
return fmt.Errorf("Duplicate validator in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress())
|
||||
}
|
||||
if val.Jailed && val.Status == sdk.Bonded {
|
||||
return fmt.Errorf("Validator is bonded and jailed in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress())
|
||||
}
|
||||
addrMap[strKey] = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -259,13 +280,13 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) {
|
|||
}
|
||||
|
||||
// GaiaAppGenState but with JSON
|
||||
func GaiaAppGenStateJSON(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
|
||||
// create the final app state
|
||||
genesisState, err := GaiaAppGenState(cdc, appGenTxs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appState, err = wire.MarshalJSONIndent(cdc, genesisState)
|
||||
appState, err = codec.MarshalJSONIndent(cdc, genesisState)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,11 +6,25 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
stake "github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
)
|
||||
|
||||
var (
|
||||
pk1 = ed25519.GenPrivKey().PubKey()
|
||||
pk2 = ed25519.GenPrivKey().PubKey()
|
||||
pk3 = ed25519.GenPrivKey().PubKey()
|
||||
addr1 = sdk.ValAddress(pk1.Address())
|
||||
addr2 = sdk.ValAddress(pk2.Address())
|
||||
addr3 = sdk.ValAddress(pk3.Address())
|
||||
|
||||
emptyAddr sdk.ValAddress
|
||||
emptyPubkey crypto.PubKey
|
||||
)
|
||||
|
||||
func makeGenesisState(genTxs []GaiaGenTx) GenesisState {
|
||||
// start with the default staking genesis state
|
||||
stakeData := stake.DefaultGenesisState()
|
||||
|
@ -63,13 +77,27 @@ func TestGaiaAppGenState(t *testing.T) {
|
|||
|
||||
func TestGaiaGenesisValidation(t *testing.T) {
|
||||
genTxs := make([]GaiaGenTx, 2)
|
||||
privKey := ed25519.GenPrivKey()
|
||||
pubKey := privKey.PubKey()
|
||||
addr := pubKey.Address()
|
||||
addr := pk1.Address()
|
||||
// Test duplicate accounts fails
|
||||
genTxs[0] = GaiaGenTx{"", sdk.AccAddress(addr), ""}
|
||||
genTxs[1] = GaiaGenTx{"", sdk.AccAddress(addr), ""}
|
||||
genesisState := makeGenesisState(genTxs)
|
||||
err := GaiaValidateGenesisState(genesisState)
|
||||
require.NotNil(t, err)
|
||||
// Test bonded + jailed validator fails
|
||||
genesisState = makeGenesisState(genTxs[:1])
|
||||
val1 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #2"})
|
||||
val1.Jailed = true
|
||||
val1.Status = sdk.Bonded
|
||||
genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val1)
|
||||
err = GaiaValidateGenesisState(genesisState)
|
||||
require.NotNil(t, err)
|
||||
// Test duplicate validator fails
|
||||
val1.Jailed = false
|
||||
genesisState = makeGenesisState(genTxs[:1])
|
||||
val2 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #3"})
|
||||
genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val1)
|
||||
genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val2)
|
||||
err = GaiaValidateGenesisState(genesisState)
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
|
|
@ -10,11 +10,9 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
|
@ -43,14 +41,14 @@ func init() {
|
|||
flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit")
|
||||
}
|
||||
|
||||
func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
|
||||
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
var genesisAccounts []GenesisAccount
|
||||
|
||||
// Randomly generate some genesis accounts
|
||||
for _, acc := range accs {
|
||||
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}}
|
||||
genesisAccounts = append(genesisAccounts, GenesisAccount{
|
||||
Address: acc,
|
||||
Address: acc.Address,
|
||||
Coins: coins,
|
||||
})
|
||||
}
|
||||
|
@ -62,10 +60,10 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
|||
// XXX Try different numbers of initially bonded validators
|
||||
numInitiallyBonded := int64(50)
|
||||
for i := 0; i < int(numInitiallyBonded); i++ {
|
||||
validator := stake.NewValidator(sdk.ValAddress(accs[i]), keys[i].PubKey(), stake.Description{})
|
||||
validator := stake.NewValidator(sdk.ValAddress(accs[i].Address), accs[i].PubKey, stake.Description{})
|
||||
validator.Tokens = sdk.NewDec(100)
|
||||
validator.DelegatorShares = sdk.NewDec(100)
|
||||
delegation := stake.Delegation{accs[i], sdk.ValAddress(accs[i]), sdk.NewDec(100), 0}
|
||||
delegation := stake.Delegation{accs[i].Address, sdk.ValAddress(accs[i].Address), sdk.NewDec(100), 0}
|
||||
validators = append(validators, validator)
|
||||
delegations = append(delegations, delegation)
|
||||
}
|
||||
|
@ -90,30 +88,28 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
|||
return appState
|
||||
}
|
||||
|
||||
func testAndRunTxs(app *GaiaApp) []simulation.Operation {
|
||||
return []simulation.Operation{
|
||||
banksim.SimulateSingleInputMsgSend(app.accountMapper),
|
||||
govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper),
|
||||
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
|
||||
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper),
|
||||
stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper),
|
||||
slashingsim.SimulateMsgUnjail(app.slashingKeeper),
|
||||
func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
|
||||
return []simulation.WeightedOperation{
|
||||
{100, banksim.SimulateSingleInputMsgSend(app.accountMapper)},
|
||||
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)},
|
||||
{100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper)},
|
||||
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper)},
|
||||
{100, stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper)},
|
||||
{100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)},
|
||||
}
|
||||
}
|
||||
|
||||
func invariants(app *GaiaApp) []simulation.Invariant {
|
||||
return []simulation.Invariant{
|
||||
func(t *testing.T, baseapp *baseapp.BaseApp, log string) {
|
||||
banksim.NonnegativeBalanceInvariant(app.accountMapper)(t, baseapp, log)
|
||||
govsim.AllInvariants()(t, baseapp, log)
|
||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.accountMapper)(t, baseapp, log)
|
||||
slashingsim.AllInvariants()(t, baseapp, log)
|
||||
},
|
||||
banksim.NonnegativeBalanceInvariant(app.accountMapper),
|
||||
govsim.AllInvariants(),
|
||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper, app.accountMapper),
|
||||
slashingsim.AllInvariants(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +130,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
|
|||
|
||||
// Run randomized simulation
|
||||
// TODO parameterize numbers, save for a later PR
|
||||
simulation.SimulateFromSeed(
|
||||
err := simulation.SimulateFromSeed(
|
||||
b, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
|
@ -143,6 +139,10 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
|
|||
blockSize,
|
||||
commit,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
b.Fail()
|
||||
}
|
||||
if commit {
|
||||
fmt.Println("GoLevelDB Stats")
|
||||
fmt.Println(db.Stats()["leveldb.stats"])
|
||||
|
@ -167,7 +167,7 @@ func TestFullGaiaSimulation(t *testing.T) {
|
|||
require.Equal(t, "GaiaApp", app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
simulation.SimulateFromSeed(
|
||||
err := simulation.SimulateFromSeed(
|
||||
t, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
|
@ -179,6 +179,7 @@ func TestFullGaiaSimulation(t *testing.T) {
|
|||
if commit {
|
||||
fmt.Println("Database Size", db.Stats()["database.size"])
|
||||
}
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
||||
|
@ -207,9 +208,9 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||
[]simulation.Invariant{},
|
||||
50,
|
||||
100,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
app.Commit()
|
||||
//app.Commit()
|
||||
appHash := app.LastCommitID().Hash
|
||||
appHashList[j] = appHash
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"path"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -18,10 +19,10 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
@ -36,6 +37,72 @@ func init() {
|
|||
gaiadHome, gaiacliHome = getTestingHomeDirs()
|
||||
}
|
||||
|
||||
func TestGaiaCLIMinimumFees(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
||||
// start gaiad server with minimum fees
|
||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --minimum_fees=2feeToken", gaiadHome, servAddr))
|
||||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
success := executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
}
|
||||
|
||||
func TestGaiaCLIFeesDeduction(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
||||
// start gaiad server with minimum fees
|
||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --minimum_fees=1fooToken", gaiadHome, servAddr))
|
||||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// test simulation
|
||||
success := executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli tx send %v --amount=1000fooToken --to=%s --from=foo --fee=1fooToken --dry-run", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// ensure state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// insufficient funds (coins + fees)
|
||||
success = executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli tx send %v --amount=1000fooToken --to=%s --from=foo --fee=1fooToken", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// ensure state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1000), fooAcc.GetCoins().AmountOf("fooToken").Int64())
|
||||
|
||||
// test success (transfer = coins + fees)
|
||||
success = executeWrite(t, fmt.Sprintf(
|
||||
"gaiacli tx send %v --fee=300fooToken --amount=500fooToken --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
}
|
||||
|
||||
func TestGaiaCLISend(t *testing.T) {
|
||||
chainID, servAddr, port := initializeFixtures(t)
|
||||
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)
|
||||
|
@ -50,40 +117,40 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// Test --dry-run
|
||||
success := executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --dry-run", flags, barAddr), app.DefaultKeyPass)
|
||||
success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo --dry-run", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
// Check state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// test autosequencing
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// test memo
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), app.DefaultKeyPass)
|
||||
executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(30), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(20), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
}
|
||||
|
||||
|
@ -101,19 +168,27 @@ func TestGaiaCLIGasAuto(t *testing.T) {
|
|||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// Test failure with auto gas disabled and very little gas set by hand
|
||||
success := executeWrite(t, fmt.Sprintf("gaiacli send %v --gas=10 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
success := executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=10 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// Check state didn't change
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
// Test failure with negative gas
|
||||
success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=-100 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
|
||||
// Test failure with 0 gas
|
||||
success = executeWrite(t, fmt.Sprintf("gaiacli tx send %v --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.False(t, success)
|
||||
|
||||
// Enable auto gas
|
||||
success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli send %v --json --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
success, stdout, _ := executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx send %v --json --gas=simulate --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
// check that gas wanted == gas used
|
||||
cdc := app.MakeCodec()
|
||||
|
@ -126,7 +201,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
|
|||
require.Equal(t, jsonOutput.Response.GasWanted, jsonOutput.Response.GasUsed)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
// Check state has changed accordingly
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
}
|
||||
|
||||
|
@ -145,12 +220,12 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
barAddr, barPubKey := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
barCeshPubKey := sdk.MustBech32ifyConsPub(barPubKey)
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
executeWrite(t, fmt.Sprintf("gaiacli tx send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
defaultParams := stake.DefaultParams()
|
||||
|
@ -159,11 +234,14 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
initialPool = initialPool.ProcessProvisions(defaultParams) // provisions are added to the pool every hour
|
||||
|
||||
// create validator
|
||||
cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags)
|
||||
cvStr := fmt.Sprintf("gaiacli tx create-validator %v", flags)
|
||||
cvStr += fmt.Sprintf(" --from=%s", "bar")
|
||||
cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey)
|
||||
cvStr += fmt.Sprintf(" --amount=%v", "2steak")
|
||||
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
|
||||
cvStr += fmt.Sprintf(" --commission-rate=%v", "0.05")
|
||||
cvStr += fmt.Sprintf(" --commission-max-rate=%v", "0.20")
|
||||
cvStr += fmt.Sprintf(" --commission-max-change-rate=%v", "0.10")
|
||||
|
||||
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1))
|
||||
|
||||
|
@ -184,15 +262,15 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
executeWrite(t, cvStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc)
|
||||
|
||||
validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", sdk.ValAddress(barAddr), flags))
|
||||
validator := executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags))
|
||||
require.Equal(t, validator.OperatorAddr, sdk.ValAddress(barAddr))
|
||||
require.True(sdk.DecEq(t, sdk.NewDec(2), validator.Tokens))
|
||||
|
||||
// unbond a single share
|
||||
unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags)
|
||||
unbondStr := fmt.Sprintf("gaiacli tx unbond begin %v", flags)
|
||||
unbondStr += fmt.Sprintf(" --from=%s", "bar")
|
||||
unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr))
|
||||
unbondStr += fmt.Sprintf(" --shares-amount=%v", "1")
|
||||
|
@ -202,16 +280,16 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
/* // this won't be what we expect because we've only started unbonding, haven't completed
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %v %v", barCech, flags))
|
||||
require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc)
|
||||
*/
|
||||
validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", sdk.ValAddress(barAddr), flags))
|
||||
validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags))
|
||||
require.Equal(t, "1.0000000000", validator.Tokens.String())
|
||||
|
||||
params := executeGetParams(t, fmt.Sprintf("gaiacli stake parameters --output=json %v", flags))
|
||||
params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags))
|
||||
require.True(t, defaultParams.Equal(params))
|
||||
|
||||
pool := executeGetPool(t, fmt.Sprintf("gaiacli stake pool --output=json %v", flags))
|
||||
pool := executeGetPool(t, fmt.Sprintf("gaiacli query pool --output=json %v", flags))
|
||||
require.Equal(t, initialPool.DateLastCommissionReset, pool.DateLastCommissionReset)
|
||||
require.Equal(t, initialPool.PrevBondedShares, pool.PrevBondedShares)
|
||||
require.Equal(t, initialPool.BondedTokens, pool.BondedTokens)
|
||||
|
@ -230,14 +308,14 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags), "")
|
||||
proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
|
||||
require.Equal(t, "No matching proposals found", proposalsQuery)
|
||||
|
||||
// submit a test proposal
|
||||
spStr := fmt.Sprintf("gaiacli gov submit-proposal %v", flags)
|
||||
spStr := fmt.Sprintf("gaiacli tx submit-proposal %v", flags)
|
||||
spStr += fmt.Sprintf(" --from=%s", "foo")
|
||||
spStr += fmt.Sprintf(" --deposit=%s", "5steak")
|
||||
spStr += fmt.Sprintf(" --type=%s", "Text")
|
||||
|
@ -261,17 +339,17 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
executeWrite(t, spStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposal-id=1 --output=json %v", flags))
|
||||
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
|
||||
require.Equal(t, int64(1), proposal1.GetProposalID())
|
||||
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
|
||||
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags), "")
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
|
||||
require.Equal(t, " 1 - Test", proposalsQuery)
|
||||
|
||||
depositStr := fmt.Sprintf("gaiacli gov deposit %v", flags)
|
||||
depositStr := fmt.Sprintf("gaiacli tx deposit %v", flags)
|
||||
depositStr += fmt.Sprintf(" --from=%s", "foo")
|
||||
depositStr += fmt.Sprintf(" --deposit=%s", "10steak")
|
||||
depositStr += fmt.Sprintf(" --proposal-id=%s", "1")
|
||||
|
@ -289,13 +367,13 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
executeWrite(t, depositStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposal-id=1 --output=json %v", flags))
|
||||
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
|
||||
require.Equal(t, int64(1), proposal1.GetProposalID())
|
||||
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
|
||||
|
||||
voteStr := fmt.Sprintf("gaiacli gov vote %v", flags)
|
||||
voteStr := fmt.Sprintf("gaiacli tx vote %v", flags)
|
||||
voteStr += fmt.Sprintf(" --from=%s", "foo")
|
||||
voteStr += fmt.Sprintf(" --proposal-id=%s", "1")
|
||||
voteStr += fmt.Sprintf(" --option=%s", "Yes")
|
||||
|
@ -313,23 +391,23 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
executeWrite(t, voteStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
vote := executeGetVote(t, fmt.Sprintf("gaiacli gov query-vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
|
||||
vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1), vote.ProposalID)
|
||||
require.Equal(t, gov.OptionYes, vote.Option)
|
||||
|
||||
votes := executeGetVotes(t, fmt.Sprintf("gaiacli gov query-votes --proposal-id=1 --output=json %v", flags))
|
||||
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags))
|
||||
require.Len(t, votes, 1)
|
||||
require.Equal(t, int64(1), votes[0].ProposalID)
|
||||
require.Equal(t, gov.OptionYes, votes[0].Option)
|
||||
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=DepositPeriod %v", flags), "")
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "")
|
||||
require.Equal(t, "No matching proposals found", proposalsQuery)
|
||||
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=VotingPeriod %v", flags), "")
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "")
|
||||
require.Equal(t, " 1 - Test", proposalsQuery)
|
||||
|
||||
// submit a second test proposal
|
||||
spStr = fmt.Sprintf("gaiacli gov submit-proposal %v", flags)
|
||||
spStr = fmt.Sprintf("gaiacli tx submit-proposal %v", flags)
|
||||
spStr += fmt.Sprintf(" --from=%s", "foo")
|
||||
spStr += fmt.Sprintf(" --deposit=%s", "5steak")
|
||||
spStr += fmt.Sprintf(" --type=%s", "Text")
|
||||
|
@ -339,7 +417,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
executeWrite(t, spStr, app.DefaultKeyPass)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --latest=1 %v", flags), "")
|
||||
proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "")
|
||||
require.Equal(t, " 2 - Apples", proposalsQuery)
|
||||
}
|
||||
|
||||
|
@ -359,7 +437,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
|
||||
// Test generate sendTx with default gas
|
||||
success, stdout, stderr := executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=10steak --to=%s --from=foo --generate-only",
|
||||
"gaiacli tx send %v --amount=10steak --to=%s --from=foo --generate-only",
|
||||
flags, barAddr), []string{}...)
|
||||
require.True(t, success)
|
||||
require.Empty(t, stderr)
|
||||
|
@ -370,7 +448,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
|
||||
// Test generate sendTx with --gas=$amount
|
||||
success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=100 --generate-only",
|
||||
"gaiacli tx send %v --amount=10steak --to=%s --from=foo --gas=100 --generate-only",
|
||||
flags, barAddr), []string{}...)
|
||||
require.True(t, success)
|
||||
require.Empty(t, stderr)
|
||||
|
@ -381,7 +459,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
|
||||
// Test generate sendTx, estimate gas
|
||||
success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=0 --generate-only",
|
||||
"gaiacli tx send %v --amount=10steak --to=%s --from=foo --gas=simulate --generate-only",
|
||||
flags, barAddr), []string{}...)
|
||||
require.True(t, success)
|
||||
require.NotEmpty(t, stderr)
|
||||
|
@ -395,13 +473,13 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
|
||||
// Test sign --print-sigs
|
||||
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli sign %v --print-sigs %v", flags, unsignedTxFile.Name()))
|
||||
"gaiacli tx sign %v --print-sigs %v", flags, unsignedTxFile.Name()))
|
||||
require.True(t, success)
|
||||
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout)
|
||||
|
||||
// Test sign
|
||||
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli sign %v --name=foo %v", flags, unsignedTxFile.Name()), app.DefaultKeyPass)
|
||||
"gaiacli tx sign %v --name=foo %v", flags, unsignedTxFile.Name()), app.DefaultKeyPass)
|
||||
require.True(t, success)
|
||||
msg = unmarshalStdTx(t, stdout)
|
||||
require.Equal(t, len(msg.Msgs), 1)
|
||||
|
@ -414,15 +492,15 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
|
||||
// Test sign --print-signatures
|
||||
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
|
||||
"gaiacli sign %v --print-sigs %v", flags, signedTxFile.Name()))
|
||||
"gaiacli tx sign %v --print-sigs %v", flags, signedTxFile.Name()))
|
||||
require.True(t, success)
|
||||
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\n", fooAddr.String(), fooAddr.String()), stdout)
|
||||
|
||||
// Test broadcast
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli broadcast %v --json %v", flags, signedTxFile.Name()))
|
||||
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf("gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name()))
|
||||
require.True(t, success)
|
||||
var result struct {
|
||||
Response abci.ResponseDeliverTx
|
||||
|
@ -432,12 +510,54 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|||
require.Equal(t, msg.Fee.Gas, result.Response.GasWanted)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
}
|
||||
|
||||
func TestGaiaCLIConfig(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(gaiacliHome))
|
||||
require.NoError(t, os.RemoveAll(gaiadHome))
|
||||
servAddr, port, err := server.FreeTCPAddr()
|
||||
require.NoError(t, err)
|
||||
node := fmt.Sprintf("%s:%s", servAddr, port)
|
||||
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome))
|
||||
executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y")
|
||||
config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
|
||||
require.NoError(t, err)
|
||||
expectedConfig := fmt.Sprintf(`chain_id = "%s"
|
||||
encoding = "btc"
|
||||
home = "%s"
|
||||
node = "%s"
|
||||
output = "text"
|
||||
trace = false
|
||||
trust_node = true
|
||||
`, chainID, gaiacliHome, node)
|
||||
require.Equal(t, expectedConfig, string(config))
|
||||
// ensure a backup gets created
|
||||
executeWrite(t, "gaiacli config", gaiacliHome, node, "y", "y")
|
||||
configBackup, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml-old"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedConfig, string(configBackup))
|
||||
|
||||
require.NoError(t, os.RemoveAll(gaiadHome))
|
||||
executeWrite(t, "gaiacli config", gaiacliHome, node, "y")
|
||||
|
||||
// ensure it works without an initialized gaiad state
|
||||
expectedConfig = fmt.Sprintf(`chain_id = ""
|
||||
encoding = "btc"
|
||||
home = "%s"
|
||||
node = "%s"
|
||||
output = "text"
|
||||
trace = false
|
||||
trust_node = true
|
||||
`, gaiacliHome, node)
|
||||
config, err = ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedConfig, string(config))
|
||||
}
|
||||
|
||||
//___________________________________________________________________________________
|
||||
// helper methods
|
||||
|
||||
|
@ -541,8 +661,8 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
|
|||
require.NoError(t, err, "out %v, err %v", out, err)
|
||||
value := initRes["value"]
|
||||
var acc auth.BaseAccount
|
||||
cdc := wire.NewCodec()
|
||||
wire.RegisterCrypto(cdc)
|
||||
cdc := codec.New()
|
||||
codec.RegisterCrypto(cdc)
|
||||
err = cdc.UnmarshalJSON(value, &acc)
|
||||
require.NoError(t, err, "value %v, err %v", string(value), err)
|
||||
return acc
|
||||
|
|
|
@ -14,11 +14,20 @@ import (
|
|||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||
govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
|
||||
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
|
||||
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"path"
|
||||
"os"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
storeAcc = "acc"
|
||||
storeGov = "gov"
|
||||
storeSlashing = "slashing"
|
||||
storeStake = "stake"
|
||||
)
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
|
@ -36,105 +45,76 @@ func main() {
|
|||
// TODO: setup keybase, viper object, etc. to be passed into
|
||||
// the below functions and eliminate global vars, like we do
|
||||
// with the cdc
|
||||
rootCmd.AddCommand(client.ConfigCmd())
|
||||
|
||||
// add standard rpc commands
|
||||
rpc.AddCommands(rootCmd)
|
||||
|
||||
//Add state commands
|
||||
tendermintCmd := &cobra.Command{
|
||||
Use: "tendermint",
|
||||
Short: "Tendermint state querying subcommands",
|
||||
//Add query commands
|
||||
queryCmd := &cobra.Command{
|
||||
Use: "query",
|
||||
Aliases: []string{"q"},
|
||||
Short: "Querying subcommands",
|
||||
}
|
||||
tendermintCmd.AddCommand(
|
||||
queryCmd.AddCommand(
|
||||
rpc.BlockCommand(),
|
||||
rpc.ValidatorCommand(),
|
||||
)
|
||||
tx.AddCommands(tendermintCmd, cdc)
|
||||
tx.AddCommands(queryCmd, cdc)
|
||||
queryCmd.AddCommand(client.LineBreak)
|
||||
queryCmd.AddCommand(client.GetCommands(
|
||||
authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
stakecmd.GetCmdQueryDelegation(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryDelegations(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryParams(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryPool(storeStake, cdc),
|
||||
govcmd.GetCmdQueryProposal(storeGov, cdc),
|
||||
govcmd.GetCmdQueryProposals(storeGov, cdc),
|
||||
stakecmd.GetCmdQueryRedelegation(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryRedelegations(storeStake, cdc),
|
||||
slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc),
|
||||
stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryValidator(storeStake, cdc),
|
||||
stakecmd.GetCmdQueryValidators(storeStake, cdc),
|
||||
govcmd.GetCmdQueryVote(storeGov, cdc),
|
||||
govcmd.GetCmdQueryVotes(storeGov, cdc),
|
||||
)...)
|
||||
|
||||
//Add IBC commands
|
||||
ibcCmd := &cobra.Command{
|
||||
Use: "ibc",
|
||||
Short: "Inter-Blockchain Communication subcommands",
|
||||
//Add query commands
|
||||
txCmd := &cobra.Command{
|
||||
Use: "tx",
|
||||
Short: "Transactions subcommands",
|
||||
}
|
||||
ibcCmd.AddCommand(
|
||||
|
||||
//Add auth and bank commands
|
||||
txCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
bankcmd.GetBroadcastCommand(cdc),
|
||||
authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
txCmd.AddCommand(client.LineBreak)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
tendermintCmd,
|
||||
ibcCmd,
|
||||
lcd.ServeCommand(cdc),
|
||||
client.LineBreak,
|
||||
)
|
||||
|
||||
//Add stake commands
|
||||
stakeCmd := &cobra.Command{
|
||||
Use: "stake",
|
||||
Short: "Stake and validation subcommands",
|
||||
}
|
||||
stakeCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
stakecmd.GetCmdQueryValidator("stake", cdc),
|
||||
stakecmd.GetCmdQueryValidators("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegation("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegations("stake", cdc),
|
||||
stakecmd.GetCmdQueryParams("stake", cdc),
|
||||
stakecmd.GetCmdQueryPool("stake", cdc),
|
||||
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc),
|
||||
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc),
|
||||
stakecmd.GetCmdQueryRedelegation("stake", cdc),
|
||||
stakecmd.GetCmdQueryRedelegations("stake", cdc),
|
||||
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
|
||||
)...)
|
||||
stakeCmd.AddCommand(
|
||||
txCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
stakecmd.GetCmdCreateValidator(cdc),
|
||||
stakecmd.GetCmdEditValidator(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond("stake", cdc),
|
||||
stakecmd.GetCmdRedelegate("stake", cdc),
|
||||
slashingcmd.GetCmdUnjail(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
stakeCmd,
|
||||
)
|
||||
|
||||
//Add stake commands
|
||||
govCmd := &cobra.Command{
|
||||
Use: "gov",
|
||||
Short: "Governance and voting subcommands",
|
||||
}
|
||||
govCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
govcmd.GetCmdQueryProposal("gov", cdc),
|
||||
govcmd.GetCmdQueryVote("gov", cdc),
|
||||
govcmd.GetCmdQueryVotes("gov", cdc),
|
||||
govcmd.GetCmdQueryProposals("gov", cdc),
|
||||
)...)
|
||||
govCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
govcmd.GetCmdSubmitProposal(cdc),
|
||||
govcmd.GetCmdDeposit(cdc),
|
||||
stakecmd.GetCmdRedelegate(storeStake, cdc),
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
govcmd.GetCmdSubmitProposal(cdc),
|
||||
stakecmd.GetCmdUnbond(storeStake, cdc),
|
||||
slashingcmd.GetCmdUnjail(cdc),
|
||||
govcmd.GetCmdVote(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
govCmd,
|
||||
queryCmd,
|
||||
txCmd,
|
||||
lcd.ServeCommand(cdc),
|
||||
client.LineBreak,
|
||||
)
|
||||
|
||||
//Add auth and bank commands
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
bankcmd.GetBroadcastCommand(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
rootCmd.AddCommand(
|
||||
keys.Commands(),
|
||||
|
@ -144,9 +124,35 @@ func main() {
|
|||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
|
||||
err := executor.Execute()
|
||||
err := initConfig(rootCmd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = executor.Execute()
|
||||
if err != nil {
|
||||
// handle with #870
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig(cmd *cobra.Command) error {
|
||||
home, err := cmd.PersistentFlags().GetString(cli.HomeFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgFile := path.Join(home, "config", "config.toml")
|
||||
if _, err := os.Stat(cfgFile); err == nil {
|
||||
viper.SetConfigFile(cfgFile)
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil {
|
||||
return err
|
||||
}
|
||||
return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag))
|
||||
}
|
||||
|
|
|
@ -43,7 +43,10 @@ func main() {
|
|||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||
return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")))
|
||||
return app.NewGaiaApp(logger, db, traceStore,
|
||||
baseapp.SetPruning(viper.GetString("pruning")),
|
||||
baseapp.SetMinimumFees(viper.GetString("minimum_fees")),
|
||||
)
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(
|
||||
|
|
|
@ -21,10 +21,9 @@ import (
|
|||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
@ -65,7 +64,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error {
|
|||
// The following powerKey was there, but the corresponding "trouble" validator did not exist.
|
||||
// So here we do a binary search on the past states to find when the powerKey first showed up ...
|
||||
|
||||
// operator of the validator the bonds, gets revoked, later unbonds, and then later is still found in the bypower store
|
||||
// operator of the validator the bonds, gets jailed, later unbonds, and then later is still found in the bypower store
|
||||
trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||
// this is his "bypower" key
|
||||
powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||
|
@ -127,13 +126,13 @@ var (
|
|||
// Extended ABCI application
|
||||
type GaiaApp struct {
|
||||
*bam.BaseApp
|
||||
cdc *wire.Codec
|
||||
cdc *codec.Codec
|
||||
|
||||
// keys to access the substores
|
||||
keyMain *sdk.KVStoreKey
|
||||
keyAccount *sdk.KVStoreKey
|
||||
keyIBC *sdk.KVStoreKey
|
||||
keyStake *sdk.KVStoreKey
|
||||
tkeyStake *sdk.TransientStoreKey
|
||||
keySlashing *sdk.KVStoreKey
|
||||
keyParams *sdk.KVStoreKey
|
||||
|
||||
|
@ -141,7 +140,6 @@ type GaiaApp struct {
|
|||
accountMapper auth.AccountMapper
|
||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||
bankKeeper bank.Keeper
|
||||
ibcMapper ibc.Mapper
|
||||
stakeKeeper stake.Keeper
|
||||
slashingKeeper slashing.Keeper
|
||||
paramsKeeper params.Keeper
|
||||
|
@ -159,8 +157,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
|||
cdc: cdc,
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||
keyStake: sdk.NewKVStoreKey("stake"),
|
||||
tkeyStake: sdk.NewTransientStoreKey("transient_stake"),
|
||||
keySlashing: sdk.NewKVStoreKey("slashing"),
|
||||
keyParams: sdk.NewKVStoreKey("params"),
|
||||
}
|
||||
|
@ -173,16 +171,14 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
|||
)
|
||||
|
||||
// add handlers
|
||||
app.bankKeeper = bank.NewKeeper(app.accountMapper)
|
||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
|
||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||
|
||||
// register message routes
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
|
@ -190,7 +186,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
|||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetEndBlocker(app.EndBlocker)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing)
|
||||
err := app.LoadLatestVersion(app.keyMain)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
|
@ -202,15 +198,14 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
|||
}
|
||||
|
||||
// custom tx codec
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
ibc.RegisterWire(cdc)
|
||||
bank.RegisterWire(cdc)
|
||||
stake.RegisterWire(cdc)
|
||||
slashing.RegisterWire(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
func MakeCodec() *codec.Codec {
|
||||
var cdc = codec.New()
|
||||
bank.RegisterCodec(cdc)
|
||||
stake.RegisterCodec(cdc)
|
||||
slashing.RegisterCodec(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
cdc.Seal()
|
||||
return cdc
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ See [testnets repo](https://github.com/cosmos/testnets).
|
|||
## *June 13, 2018, 17:00 EST* - Gaia-6002 is making blocks!
|
||||
|
||||
- Gaia-6002 is live and making blocks
|
||||
- Absent validators have been slashed and revoked
|
||||
- Absent validators have been slashed and jailed
|
||||
- Currently live with 17 validators
|
||||
|
||||
## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package wire
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
// amino codec to marshal/unmarshal
|
||||
type Codec = amino.Codec
|
||||
|
||||
func NewCodec() *Codec {
|
||||
func New() *Codec {
|
||||
cdc := amino.NewCodec()
|
||||
return cdc
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
|||
var Cdc *Codec
|
||||
|
||||
func init() {
|
||||
cdc := NewCodec()
|
||||
cdc := New()
|
||||
RegisterCrypto(cdc)
|
||||
Cdc = cdc.Seal()
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var _ Keybase = dbKeybase{}
|
||||
|
@ -41,6 +42,8 @@ const (
|
|||
French
|
||||
// Italian is currently not supported.
|
||||
Italian
|
||||
addressSuffix = "address"
|
||||
infoSuffix = "info"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -179,11 +182,16 @@ func (kb dbKeybase) List() ([]Info, error) {
|
|||
iter := kb.db.Iterator(nil, nil)
|
||||
defer iter.Close()
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
info, err := readInfo(iter.Value())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
key := string(iter.Key())
|
||||
|
||||
// need to include only keys in storage that have an info suffix
|
||||
if strings.HasSuffix(key, infoSuffix) {
|
||||
info, err := readInfo(iter.Value())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, info)
|
||||
}
|
||||
res = append(res, info)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -197,6 +205,15 @@ func (kb dbKeybase) Get(name string) (Info, error) {
|
|||
return readInfo(bs)
|
||||
}
|
||||
|
||||
func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) {
|
||||
ik := kb.db.Get(addrKey(address))
|
||||
if len(ik) == 0 {
|
||||
return nil, fmt.Errorf("key with address %s not found", address)
|
||||
}
|
||||
bs := kb.db.Get(ik)
|
||||
return readInfo(bs)
|
||||
}
|
||||
|
||||
// Sign signs the msg with the named key.
|
||||
// It returns an error if the key doesn't exist or the decryption fails.
|
||||
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
|
||||
|
@ -347,6 +364,7 @@ func (kb dbKeybase) Delete(name, passphrase string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kb.db.DeleteSync(addrKey(linfo.GetAddress()))
|
||||
kb.db.DeleteSync(infoKey(name))
|
||||
return nil
|
||||
case ledgerInfo:
|
||||
|
@ -354,9 +372,11 @@ func (kb dbKeybase) Delete(name, passphrase string) error {
|
|||
if passphrase != "yes" {
|
||||
return fmt.Errorf("enter 'yes' exactly to delete the key - this cannot be undone")
|
||||
}
|
||||
kb.db.DeleteSync(addrKey(info.GetAddress()))
|
||||
kb.db.DeleteSync(infoKey(name))
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -413,9 +433,16 @@ func (kb dbKeybase) writeOfflineKey(pub tmcrypto.PubKey, name string) Info {
|
|||
|
||||
func (kb dbKeybase) writeInfo(info Info, name string) {
|
||||
// write the info by key
|
||||
kb.db.SetSync(infoKey(name), writeInfo(info))
|
||||
key := infoKey(name)
|
||||
kb.db.SetSync(key, writeInfo(info))
|
||||
// store a pointer to the infokey by address for fast lookup
|
||||
kb.db.SetSync(addrKey(info.GetAddress()), key)
|
||||
}
|
||||
|
||||
func addrKey(address types.AccAddress) []byte {
|
||||
return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix))
|
||||
}
|
||||
|
||||
func infoKey(name string) []byte {
|
||||
return []byte(fmt.Sprintf("%s.info", name))
|
||||
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -20,8 +21,9 @@ func init() {
|
|||
// TestKeyManagement makes sure we can manipulate these keys well
|
||||
func TestKeyManagement(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
db := dbm.NewMemDB()
|
||||
cstore := New(
|
||||
dbm.NewMemDB(),
|
||||
db,
|
||||
)
|
||||
|
||||
algo := Secp256k1
|
||||
|
@ -51,6 +53,12 @@ func TestKeyManagement(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
_, err = cstore.Get(n3)
|
||||
require.NotNil(t, err)
|
||||
_, err = cstore.GetByAddress(accAddr(i2))
|
||||
require.NoError(t, err)
|
||||
addr, err := types.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t")
|
||||
require.NoError(t, err)
|
||||
_, err = cstore.GetByAddress(addr)
|
||||
require.NotNil(t, err)
|
||||
|
||||
// list shows them in order
|
||||
keyS, err := cstore.List()
|
||||
|
@ -92,6 +100,11 @@ func TestKeyManagement(t *testing.T) {
|
|||
keyS, err = cstore.List()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(keyS))
|
||||
|
||||
// addr cache gets nuked
|
||||
err = cstore.Delete(n2, p2)
|
||||
require.NoError(t, err)
|
||||
require.False(t, db.Has(addrKey(i2.GetAddress())))
|
||||
}
|
||||
|
||||
// TestSignVerify does some detailed checks on how we sign and validate
|
||||
|
@ -387,3 +400,7 @@ func ExampleNew() {
|
|||
// Carl
|
||||
// signed by Bob
|
||||
}
|
||||
|
||||
func accAddr(info Info) types.AccAddress {
|
||||
return (types.AccAddress)(info.GetPubKey().Address())
|
||||
}
|
|
@ -5,14 +5,15 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Keybase exposes operations on a generic keystore
|
||||
type Keybase interface {
|
||||
|
||||
// CRUD on the keystore
|
||||
List() ([]Info, error)
|
||||
Get(name string) (Info, error)
|
||||
GetByAddress(address types.AccAddress) (Info, error)
|
||||
Delete(name, passphrase string) error
|
||||
|
||||
// Sign some bytes, looking up the private key to use
|
||||
|
@ -73,6 +74,8 @@ type Info interface {
|
|||
GetName() string
|
||||
// Public key
|
||||
GetPubKey() crypto.PubKey
|
||||
// Address
|
||||
GetAddress() types.AccAddress
|
||||
}
|
||||
|
||||
var _ Info = &localInfo{}
|
||||
|
@ -106,6 +109,10 @@ func (i localInfo) GetPubKey() crypto.PubKey {
|
|||
return i.PubKey
|
||||
}
|
||||
|
||||
func (i localInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// ledgerInfo is the public information about a Ledger key
|
||||
type ledgerInfo struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -133,6 +140,10 @@ func (i ledgerInfo) GetPubKey() crypto.PubKey {
|
|||
return i.PubKey
|
||||
}
|
||||
|
||||
func (i ledgerInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// offlineInfo is the public information about an offline key
|
||||
type offlineInfo struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -158,6 +169,10 @@ func (i offlineInfo) GetPubKey() crypto.PubKey {
|
|||
return i.PubKey
|
||||
}
|
||||
|
||||
func (i offlineInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// encoding info
|
||||
func writeInfo(i Info) []byte {
|
||||
return cdc.MustMarshalBinary(i)
|
||||
|
|
|
@ -24,8 +24,8 @@ module.exports = {
|
|||
children: [
|
||||
"/getting-started/voyager",
|
||||
"/getting-started/installation",
|
||||
"/getting-started/full-node",
|
||||
"/getting-started/create-testnet"
|
||||
"/getting-started/join-testnet",
|
||||
"/getting-started/networks"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -57,7 +57,9 @@ module.exports = {
|
|||
{
|
||||
title: "Lotion JS",
|
||||
collapsable: false,
|
||||
children: [["/lotion/overview", "Overview"], "/lotion/building-an-app"]
|
||||
children: [
|
||||
["/lotion/overview", "Overview"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Validators",
|
|
@ -0,0 +1,3 @@
|
|||
export default ({ router }) => {
|
||||
router.addRoutes([{ path: "/testnet/", redirect: "/" }])
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
$accentColor = #304DE9
|
||||
$textColor = #15192C
|
||||
$borderColor = #eaecef
|
||||
$codeBgColor = #282c34
|
|
@ -1,16 +1,94 @@
|
|||
# Documentation Maintenance Overview
|
||||
# Docs Build Workflow
|
||||
|
||||
The documentation found in this directory is hosted at:
|
||||
The documentation for the Cosmos SDK is hosted at:
|
||||
|
||||
- https://cosmos.network/docs/
|
||||
- https://cosmos.network/docs/ and
|
||||
- https://cosmos-staging.interblock.io/docs/
|
||||
|
||||
and built using [VuePress](https://vuepress.vuejs.org/) from the Cosmos website repo:
|
||||
built from the files in this (`/docs`) directory for
|
||||
[master](https://github.com/cosmos/cosmos-sdk/tree/master/docs)
|
||||
and [develop](https://github.com/cosmos/cosmos-sdk/tree/develop/docs),
|
||||
respectively.
|
||||
|
||||
- https://github.com/cosmos/cosmos.network
|
||||
## How It Works
|
||||
|
||||
Under the hood, Jenkins listens for changes (on develop or master) in ./docs then rebuilds
|
||||
either the staging or production site depending on which branch the changes were made.
|
||||
There is a Jenkins job listening for changes in the `/docs` directory, on both
|
||||
the `master` and `develop` branches. Any updates to files in this directory
|
||||
on those branches will automatically trigger a website deployment. Under the hood,
|
||||
a private website repository has make targets consumed by a standard Jenkins task.
|
||||
|
||||
To update the Table of Contents (layout of the documentation sidebar), edit the
|
||||
`config.js` in this directory, while the `README.md` is the landing page for the
|
||||
website documentation.
|
||||
## README
|
||||
|
||||
The [README.md](./README.md) is also the landing page for the documentation
|
||||
on the website.
|
||||
|
||||
## Config.js
|
||||
|
||||
The [config.js](./.vuepress/config.js) generates the sidebar and Table of Contents
|
||||
on the website docs. Note the use of relative links and the omission of
|
||||
file extensions. Additional features are available to improve the look
|
||||
of the sidebar.
|
||||
|
||||
## Links
|
||||
|
||||
**NOTE:** Strongly consider the existing links - both within this directory
|
||||
and to the website docs - when moving or deleting files.
|
||||
|
||||
Relative links should be used nearly everywhere, having discovered and weighed the following:
|
||||
|
||||
### Relative
|
||||
|
||||
Where is the other file, relative to the current one?
|
||||
|
||||
- works both on GitHub and for the VuePress build
|
||||
- confusing / annoying to have things like: `../../../../myfile.md`
|
||||
- requires more updates when files are re-shuffled
|
||||
|
||||
### Absolute
|
||||
|
||||
Where is the other file, given the root of the repo?
|
||||
|
||||
- works on GitHub, doesn't work for the VuePress build
|
||||
- this is much nicer: `/docs/hereitis/myfile.md`
|
||||
- if you move that file around, the links inside it are preserved (but not to it, of course)
|
||||
|
||||
### Full
|
||||
|
||||
The full GitHub URL to a file or directory. Used occasionally when it makes sense
|
||||
to send users to the GitHub.
|
||||
|
||||
## Building Locally
|
||||
|
||||
To build and serve the documentation locally, run:
|
||||
|
||||
```
|
||||
npm install -g vuepress
|
||||
```
|
||||
|
||||
then change the following line in the `config.js`:
|
||||
|
||||
```
|
||||
base: "/docs/",
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```
|
||||
base: "/",
|
||||
```
|
||||
|
||||
Finally, go up one directory to the root of the repo and run:
|
||||
|
||||
```
|
||||
# from root of repo
|
||||
vuepress build docs
|
||||
cd dist/docs
|
||||
python -m SimpleHTTPServer 8080
|
||||
```
|
||||
|
||||
then navigate to localhost:8080 in your browser.
|
||||
|
||||
## Consistency
|
||||
|
||||
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).
|
||||
|
|
|
@ -7,3 +7,8 @@ Cosmos is a decentralized network of independent parallel blockchains, each powe
|
|||
The first blockchain in the Cosmos Network is the Cosmos Hub, whose native token is the Atom. Cosmos is a permission-less network, meaning that anybody can build a blockchain on it.
|
||||
|
||||
Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary.
|
||||
|
||||
## Edit the Documentation
|
||||
|
||||
See [this file](./DOCS_README.md) for details of the build process and
|
||||
considerations when making changes.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Architecture Decision Records (ADR)
|
||||
|
||||
This is a location to record all high-level architecture decisions in the cosmos-sdk project.
|
||||
|
||||
You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t).
|
||||
|
||||
An ADR should provide:
|
||||
|
||||
- Context on the relevant goals and the current state
|
||||
- Proposed changes to achieve the goals
|
||||
- Summary of pros and cons
|
||||
- References
|
||||
- Changelog
|
||||
|
||||
Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and
|
||||
justification for a change in architecture, or for the architecture of something
|
||||
new. The spec is much more compressed and streamlined summary of everything as
|
||||
it stands today.
|
||||
|
||||
If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match.
|
||||
|
||||
Note the context/background should be written in the present tense.
|
|
@ -0,0 +1,54 @@
|
|||
# ADR 001: Global Message Counter
|
||||
|
||||
## Context
|
||||
|
||||
There is a desire for modules to have a concept of orderings between messages.
|
||||
|
||||
One such example is in staking, we currently use an "intra bond tx counter" and
|
||||
bond height.
|
||||
The purpose these two serve is to providing an ordering for validators with equal stake,
|
||||
for usage in the power-ranking of validators.
|
||||
We can't use address here, as that would create a bad incentive to grind
|
||||
addresses that optimized the sort function, which lowers the private key's
|
||||
security.
|
||||
Instead we order by whose transaction appeared first, as tracked by bondHeight
|
||||
and intra bond tx counter.
|
||||
|
||||
This logic however should not be unique to staking.
|
||||
It is very conceivable that many modules in the future will want to be able to
|
||||
know the ordering of messages / objects after they were initially created.
|
||||
|
||||
## Decision
|
||||
|
||||
Create a global message counter field of type int64.
|
||||
Note that with int64's, there is no fear of overflow under normal use,
|
||||
as it is only getting incremented by one,
|
||||
and thus has a space of 9 quintillion values to go through.
|
||||
|
||||
This counter must be persisted in state, but can just be read and written on
|
||||
begin/end block respectively.
|
||||
This field will get incremented upon every DeliverTx,
|
||||
regardless if the transaction succeeds or not.
|
||||
It must also be incremented within the check state for CheckTx.
|
||||
The global message ordering field should be set within the context
|
||||
so that modules can access it.
|
||||
|
||||
## Corollary - Intra block ordering
|
||||
In the event that there is desire to just have an intra block msg counter,
|
||||
this can easily be derived from the global message counter.
|
||||
Simply subtract current counter from first global message counter in the block.
|
||||
Thus the relevant module could easily implement this.
|
||||
|
||||
## Status
|
||||
Proposed
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
* Moves message ordering out of the set of things staking must keep track of
|
||||
* Abstracts the logic well so other modules can use it
|
||||
|
||||
### Negative
|
||||
* Another thing to implement prelaunch. (Though this should be easy to implement)
|
||||
|
||||
### Neutral
|
|
@ -0,0 +1,32 @@
|
|||
# ADR {ADR-NUMBER}: {TITLE}
|
||||
|
||||
## Changelog
|
||||
* {date}: {changelog}
|
||||
|
||||
## Context
|
||||
> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution.
|
||||
|
||||
## Decision
|
||||
> This section explains all of the details of the proposed solution, including implementation details.
|
||||
It should also describe affects / corollary items that may need to be changed as a part of this.
|
||||
If the proposed change will be large, please also indicate a way to do the change to maximize ease of review.
|
||||
(e.g. the optimal split of things to do between separate PR's)
|
||||
|
||||
## Status
|
||||
> A decision may be "proposed" if it hasn't been agreed upon yet, or "accepted" once it is agreed upon. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement.
|
||||
|
||||
{Deprecated|Proposed|Accepted}
|
||||
|
||||
## Consequences
|
||||
> This section describes the consequences, after applying the decision. All consequences should be summarized here, not just the "positive" ones.
|
||||
|
||||
### Positive
|
||||
|
||||
### Negative
|
||||
|
||||
### Neutral
|
||||
|
||||
## References
|
||||
> Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here!
|
||||
|
||||
* {reference link}
|
|
@ -23,7 +23,7 @@ NAME: TYPE: ADDRESS: PUBKEY:
|
|||
This key will only be accessible while the Ledger is plugged in and unlocked. To send some coins with this key, run the following:
|
||||
|
||||
```bash
|
||||
$ gaiacli send --from {{ .Key.Name }} --to {{ .Destination.AccAddr }} --chain-id=gaia-7000
|
||||
$ gaiacli tx send --from {{ .Key.Name }} --to {{ .Destination.AccAddr }} --chain-id=gaia-7000
|
||||
```
|
||||
|
||||
You will be asked to review and confirm the transaction on the Ledger. Once you do this you should see the result in the console! Now you can use your Ledger to manage your Atoms and Stake!
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
# Integrate a Cosmos-SDK based blockchain as a Service Provider
|
||||
|
||||
We define 'service providers' as entities providing services for end-users that involve some form of interaction with a Cosmos-SDK based blockchain (this includes the Cosmos Hub). More specifically, this document will be focused around interactions with tokens.
|
||||
|
||||
This section does not concern wallet builders that want to provide [Light-Client](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/light) functionalities. Service providers are expected to act as trusted point of contact to the blockchain for their end-users.
|
||||
|
||||
## High-level description of the architecture
|
||||
|
||||
There are three main pieces to consider:
|
||||
|
||||
- Full-nodes: To interact with the blockchain.
|
||||
- Rest Server: This acts as a relayer for HTTP calls.
|
||||
- Rest API: Define available endpoints for the Rest Server.
|
||||
|
||||
## Running a Full-Node
|
||||
|
||||
### Installation and configuration
|
||||
|
||||
We will describe the steps to run and interract with a full-node for the Cosmos Hub. For other SDK-based blockchain, the process should be similar.
|
||||
|
||||
First, you need to [install the software](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/getting-started/installation.md).
|
||||
|
||||
Then, you can start [running a full-node](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/getting-started/full-node.md).
|
||||
|
||||
### Command-Line interface
|
||||
|
||||
Next you will find a few useful CLI commands to interact with the Full-Node.
|
||||
|
||||
#### Creating a key-pair
|
||||
|
||||
To generate a new key (default ed25519 elliptic curve):
|
||||
|
||||
```bash
|
||||
gaiacli keys add <your_key_name>
|
||||
```
|
||||
|
||||
You will be asked to create a passwords (at least 8 characters) for this key-pair. The command returns 4 informations:
|
||||
|
||||
- `NAME`: Name of your key
|
||||
- `ADDRESS`: Your address. Used to receive funds.
|
||||
- `PUBKEY`: Your public key. Useful for validators.
|
||||
- `Seed phrase`: 12-words phrase. **Save this seed phrase somewhere safe**. It is used to recover your private key in case you forget the password.
|
||||
|
||||
You can see all your available keys by typing:
|
||||
|
||||
```bash
|
||||
gaiacli keys list
|
||||
```
|
||||
|
||||
#### Checking your balance
|
||||
|
||||
After receiving tokens to your address, you can view your account's balance by typing:
|
||||
|
||||
```bash
|
||||
gaiacli account <YOUR_ADDRESS>
|
||||
```
|
||||
|
||||
*Note: When you query an account balance with zero tokens, you will get this error: No account with address <YOUR_ADDRESS> was found in the state. This is expected! We're working on improving our error messages.*
|
||||
|
||||
#### Sending coins via the CLI
|
||||
|
||||
Here is the command to send coins via the CLI:
|
||||
|
||||
```bash
|
||||
gaiacli tx send --amount=10faucetToken --chain-id=<name_of_testnet_chain> --name=<key_name> --to=<destination_address>
|
||||
```
|
||||
|
||||
Flags:
|
||||
- `--amount`: This flag accepts the format `<value|coinName>`.
|
||||
- `--chain-id`: This flag allows you to specify the id of the chain. There will be different ids for different testnet chains and main chain.
|
||||
- `--name`: Name of the key of the sending account.
|
||||
- `--to`: Address of the recipient.
|
||||
|
||||
#### Help
|
||||
|
||||
If you need to do something else, the best command you can run is:
|
||||
|
||||
```bash
|
||||
gaiacli
|
||||
```
|
||||
|
||||
It will display all the available commands. For each command, you can use the `--help` flag to get further information.
|
||||
|
||||
## Setting up the Rest Server
|
||||
|
||||
The Rest Server acts as an intermediary between the front-end and the full-node. You don't need to run the Rest Server on the same machine as the full-node. If you intend to run the Rest Server on another machine, you need to go through the [Installation and configuration](#installation-and-configuration) again on this machine.
|
||||
|
||||
To start the Rest server:
|
||||
|
||||
```bash
|
||||
gaiacli rest-server --trust-node=false --node=<full_node_address:full_node_port>
|
||||
```
|
||||
|
||||
Flags:
|
||||
- `--trust-node`: A boolean. If `true`, light-client verification is enabled. If `false`, it is disabled. For service providers, this should be set to `false`.
|
||||
- `--node`: This is where you indicate the address and the port of your full-node. The format is <full_node_address:full_node_port>. If the full-node is on the same machine, the address should be "tcp://localhost".
|
||||
- `--laddr`: This flag allows you to specify the address and port for the Rest Server. You will mostly use this flag only to specify the port, in which case just input "localhost" for the address. The format is <rest_server_address:port>.
|
||||
|
||||
### Listening for incoming transaction
|
||||
|
||||
The recommended way to listen for incoming transaction is to periodically query the blockchain through the following endpoint of the LCD:
|
||||
|
||||
[`/bank/balance/{account}`](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#bankbalanceaccount---get)
|
||||
|
||||
## Rest API
|
||||
|
||||
The Rest API documents all the available endpoints that you can use to interract with your full node. It can be found [here](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md).
|
||||
|
||||
The API is divided into ICS standards for each category of endpoints. For example, the [ICS20](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#ics20---tokenapi) describes the API to interact with tokens.
|
||||
|
||||
To give more flexibility to implementers, we have separated the different steps that are involved in the process of sending transactions. You will be able to generate unsigned transactions (example with [coin transfer](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-banktransfers)), [sign](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-authtxsign) and [broadcast](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/light/api.md#post-authtxbroadcast) them with different API endpoints. This allows service providers to use their own signing mechanism for instance.
|
|
@ -1,27 +0,0 @@
|
|||
## Create your Own Testnet
|
||||
|
||||
To create your own testnet, first each validator will need to install gaiad and run gen-tx
|
||||
|
||||
```bash
|
||||
gaiad init gen-tx --name <account_name>
|
||||
```
|
||||
|
||||
This populations `$HOME/.gaiad/gen-tx/` with a json file.
|
||||
|
||||
Now these json files need to be aggregated together via Github, a Google form, pastebin or other methods.
|
||||
|
||||
Place all files on one computer in `$HOME/.gaiad/gen-tx/`
|
||||
|
||||
```bash
|
||||
gaiad init --with-txs -o --chain=<chain-name>
|
||||
```
|
||||
|
||||
This will generate a `genesis.json` in `$HOME/.gaiad/config/genesis.json` distribute this file to all validators on your testnet.
|
||||
|
||||
### Export state
|
||||
|
||||
To export state and reload (useful for testing purposes):
|
||||
|
||||
```
|
||||
gaiad export > genesis.json; cp genesis.json ~/.gaiad/config/genesis.json; gaiad start
|
||||
```
|
|
@ -29,6 +29,19 @@ You can edit this `name` later, in the `~/.gaiad/config/config.toml` file:
|
|||
moniker = "<your_custom_name>"
|
||||
```
|
||||
|
||||
You can edit the `~/.gaiad/config/gaiad.toml` file in order to enable the anti spam mechanism and reject incoming transactions with less than a minimum fee:
|
||||
|
||||
```
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
##### main base config options #####
|
||||
|
||||
# Validators reject any tx from the mempool with less than the minimum fee per gas.
|
||||
minimum_fees = ""
|
||||
```
|
||||
|
||||
|
||||
Your full node has been initialized! Please skip to [Genesis & Seeds](#genesis-seeds).
|
||||
|
||||
## Upgrading From Previous Testnet
|
|
@ -0,0 +1,209 @@
|
|||
# Networks
|
||||
|
||||
There are a variety of ways to setup either local or remote networks with automation, detailed below.
|
||||
All the required files are found in the [networks directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks) and additionally the `local` or `remote` sub-directories.
|
||||
|
||||
## Local Testnet
|
||||
|
||||
From the [networks/local directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks/local):
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Install gaia](https://cosmos.network/docs/getting-started/installation.html)
|
||||
- [Install docker](https://docs.docker.com/engine/installation/)
|
||||
- [Install docker-compose](https://docs.docker.com/compose/install/)
|
||||
|
||||
### Build
|
||||
|
||||
Build the `gaiad` binary and the `tendermint/gaiadnode` docker image.
|
||||
|
||||
Note the binary will be mounted into the container so it can be updated without
|
||||
rebuilding the image.
|
||||
|
||||
```
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
|
||||
# Build the linux binary in ./build
|
||||
make build-linux
|
||||
|
||||
# Build tendermint/gaiadnode image
|
||||
make build-docker-gaiadnode
|
||||
```
|
||||
|
||||
### Run a testnet
|
||||
|
||||
To start a 4 node testnet run:
|
||||
|
||||
```
|
||||
make localnet-start
|
||||
```
|
||||
|
||||
This command creates a 4-node network using the gaiadnode image.
|
||||
The ports for each node are found in this table:
|
||||
|
||||
| Node ID | P2P Port | RPC Port |
|
||||
| --------|-------|------|
|
||||
| `gaianode0` | `26656` | `26657` |
|
||||
| `gaianode1` | `26659` | `26660` |
|
||||
| `gaianode2` | `26661` | `26662` |
|
||||
| `gaianode3` | `26663` | `26664` |
|
||||
|
||||
To update the binary, just rebuild it and restart the nodes:
|
||||
|
||||
```
|
||||
make build-linux localnet-stop localnet-start
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `gaiad testnet` command.
|
||||
This outputs a handful of files in the `./build` directory:
|
||||
|
||||
```tree -L 2 build/
|
||||
build/
|
||||
├── gaiacli
|
||||
├── gaiad
|
||||
├── gentxs
|
||||
│ ├── node0.json
|
||||
│ ├── node1.json
|
||||
│ ├── node2.json
|
||||
│ └── node3.json
|
||||
├── node0
|
||||
│ ├── gaiacli
|
||||
│ │ ├── key_seed.json
|
||||
│ │ └── keys
|
||||
│ └── gaiad
|
||||
│ ├── ${LOG:-gaiad.log}
|
||||
│ ├── config
|
||||
│ └── data
|
||||
├── node1
|
||||
│ ├── gaiacli
|
||||
│ │ └── key_seed.json
|
||||
│ └── gaiad
|
||||
│ ├── ${LOG:-gaiad.log}
|
||||
│ ├── config
|
||||
│ └── data
|
||||
├── node2
|
||||
│ ├── gaiacli
|
||||
│ │ └── key_seed.json
|
||||
│ └── gaiad
|
||||
│ ├── ${LOG:-gaiad.log}
|
||||
│ ├── config
|
||||
│ └── data
|
||||
└── node3
|
||||
├── gaiacli
|
||||
│ └── key_seed.json
|
||||
└── gaiad
|
||||
├── ${LOG:-gaiad.log}
|
||||
├── config
|
||||
└── data
|
||||
```
|
||||
|
||||
Each `./build/nodeN` directory is mounted to the `/gaiad` directory in each container.
|
||||
|
||||
### Logging
|
||||
|
||||
Logs are saved under each `./build/nodeN/gaiad/gaia.log`. Watch them stream in with, for example:
|
||||
|
||||
```
|
||||
tail -f build/node0/gaiad/gaia.log
|
||||
```
|
||||
|
||||
### Special binaries
|
||||
|
||||
If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. For example:
|
||||
|
||||
```
|
||||
# Run with custom binary
|
||||
BINARY=gaiafoo make localnet-start
|
||||
```
|
||||
|
||||
## Remote Testnet
|
||||
|
||||
The following should be run from the [networks directory](https://github.com/cosmos/cosmos-sdk/tree/develop/networks).
|
||||
|
||||
### Terraform & Ansible
|
||||
|
||||
Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on AWS then
|
||||
[Ansible](http://www.ansible.com/) to create and manage testnets on those servers.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install [Terraform](https://www.terraform.io/downloads.html) and [Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) on a Linux machine.
|
||||
- Create an [AWS API token](https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html) with EC2 create capability.
|
||||
- Create SSH keys
|
||||
|
||||
```
|
||||
export AWS_ACCESS_KEY_ID="2345234jk2lh4234"
|
||||
export AWS_SECRET_ACCESS_KEY="234jhkg234h52kh4g5khg34"
|
||||
export TESTNET_NAME="remotenet"
|
||||
export CLUSTER_NAME= "remotenetvalidators"
|
||||
export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa"
|
||||
export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub"
|
||||
```
|
||||
|
||||
These will be used by both `terraform` and `ansible`.
|
||||
|
||||
### Create a remote network
|
||||
|
||||
```
|
||||
SERVERS=1 REGION_LIMIT=1 make validators-start
|
||||
```
|
||||
|
||||
The testnet name is what's going to be used in --chain-id, while the cluster name is the administrative tag in AWS for the servers. The code will create SERVERS amount of servers in each availability zone up to the number of REGION_LIMITs, starting at us-east-2. (us-east-1 is excluded.) The below BaSH script does the same, but sometimes it's more comfortable for input.
|
||||
|
||||
```
|
||||
./new-testnet.sh "$TESTNET_NAME" "$CLUSTER_NAME" 1 1
|
||||
```
|
||||
|
||||
### Quickly see the /status endpoint
|
||||
|
||||
```
|
||||
make validators-status
|
||||
```
|
||||
|
||||
### Delete servers
|
||||
|
||||
```
|
||||
make validators-stop
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on [this page](https://app.logz.io/#/dashboard/data-sources/Filebeat), then:
|
||||
|
||||
```
|
||||
yum install systemd-devel || echo "This will only work on RHEL-based systems."
|
||||
apt-get install libsystemd-dev || echo "This will only work on Debian-based systems."
|
||||
|
||||
go get github.com/mheese/journalbeat
|
||||
ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
You can install the DataDog agent with:
|
||||
|
||||
```
|
||||
make datadog-install
|
||||
```
|
||||
|
||||
### Single-node testnet
|
||||
|
||||
To create a single node testnet:
|
||||
|
||||
```
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
|
||||
# Clear the build folder
|
||||
rm -rf ./build
|
||||
|
||||
# Build binary
|
||||
make build-linux
|
||||
|
||||
# Create configuration
|
||||
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet -o . --v 1
|
||||
|
||||
# Run the node
|
||||
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode
|
||||
```
|
|
@ -763,7 +763,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te
|
|||
"chain_id": "string",
|
||||
"account_number": 0,
|
||||
"sequence": 0,
|
||||
"gas": 0
|
||||
"gas": "simulate"
|
||||
},
|
||||
"depositer": "string",
|
||||
"amount": 0,
|
||||
|
@ -866,7 +866,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te
|
|||
"chain_id": "string",
|
||||
"account_number": 0,
|
||||
"sequence": 0,
|
||||
"gas": 0
|
||||
"gas": "simulate"
|
||||
},
|
||||
// A cosmos address
|
||||
"voter": "string",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Getting Started
|
||||
|
||||
To start a rest server, we need to specify the following parameters:
|
||||
To start a REST server, we need to specify the following parameters:
|
||||
| Parameter | Type | Default | Required | Description |
|
||||
| ----------- | --------- | ----------------------- | -------- | ---------------------------------------------------- |
|
||||
| chain-id | string | null | true | chain id of the full node to connect |
|
||||
|
@ -12,9 +12,25 @@ To start a rest server, we need to specify the following parameters:
|
|||
Sample command:
|
||||
|
||||
```bash
|
||||
gaiacli light-client --chain-id=test --laddr=tcp://localhost:1317 --node tcp://localhost:46657 --trust-node=false
|
||||
gaiacli rest-server --chain-id=test \
|
||||
--laddr=tcp://localhost:1317 \
|
||||
--node tcp://localhost:46657 \
|
||||
--trust-node=false
|
||||
```
|
||||
|
||||
The server listens on HTTPS by default. You can set the SSL certificate to be used by the server with these additional flags:
|
||||
|
||||
```bash
|
||||
gaiacli rest-server --chain-id=test \
|
||||
--laddr=tcp://localhost:1317 \
|
||||
--node tcp://localhost:46657 \
|
||||
--trust-node=false \
|
||||
--certfile=mycert.pem --keyfile=mykey.key
|
||||
```
|
||||
|
||||
If no certificate/keyfile pair is supplied, a self-signed certificate will be generated and its fingerprint printed out.
|
||||
Append `--insecure` to the command line if you want to disable the secure layer and listen on an insecure HTTP port.
|
||||
|
||||
## Gaia Light Use Cases
|
||||
|
||||
LCD could be very helpful for related service providers. For a wallet service provider, LCD could
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# Building an App
|
||||
|
||||
::: tip
|
||||
Lotion requires __node v7.6.0__ or higher, and a mac or linux machine.
|
||||
:::
|
||||
|
||||
## Installation
|
||||
```
|
||||
$ npm install lotion
|
||||
```
|
||||
|
||||
## Simple App
|
||||
`app.js`:
|
||||
```js
|
||||
let lotion = require('lotion')
|
||||
|
||||
let app = lotion({
|
||||
initialState: {
|
||||
count: 0
|
||||
}
|
||||
})
|
||||
|
||||
app.use(function (state, tx) {
|
||||
if(state.count === tx.nonce) {
|
||||
state.count++
|
||||
}
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
run `node app.js`, then:
|
||||
```bash
|
||||
$ curl http://localhost:3000/state
|
||||
# { "count": 0 }
|
||||
|
||||
$ curl http://localhost:3000/txs -d '{ "nonce": 0 }'
|
||||
# { "ok": true }
|
||||
|
||||
$ curl http://localhost:3000/state
|
||||
# { "count": 1 }
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more about Lotion JS by visiting Lotion on [Github](https://github.com/keppel/lotion).
|
|
@ -1,5 +1,54 @@
|
|||
# Lotion JS Overview
|
||||
# Overview
|
||||
|
||||
Lotion is a new way to create blockchain apps in JavaScript, which aims to make writing new blockchains fast and fun. It builds on top of Tendermint using the ABCI protocol. Lotion lets you write secure, scalable applications that can easily interoperate with other blockchains on the Cosmos Network using IBC.
|
||||
Lotion is an alternative to the Cosmos SDK and allows you to create blockchain apps in JavaScript. It aims to make writing new blockchain apps fast and easy by using the ABCI protocol to build on top of Tendermint. Lotion lets you write secure, scalable applications that can easily interoperate with other blockchains on the Cosmos Network using IBC.
|
||||
|
||||
Lotion itself is a tiny framework; its true power comes from the network of small, focused modules built upon it. Adding a fully-featured cryptocurrency to your blockchain, for example, takes only a few lines of code.
|
||||
|
||||
For more information see the [website](https://lotionjs.com) and [GitHub repo](https://github.com/keppel/lotion), for complete documentation which expands on the following example.
|
||||
|
||||
## Building an App
|
||||
|
||||
### Installation
|
||||
|
||||
::: tip
|
||||
Lotion requires __node v7.6.0__ or higher, and a mac or linux machine.
|
||||
:::
|
||||
|
||||
```
|
||||
$ npm install lotion
|
||||
```
|
||||
|
||||
### Simple App
|
||||
|
||||
`app.js`:
|
||||
|
||||
```js
|
||||
let lotion = require('lotion')
|
||||
|
||||
let app = lotion({
|
||||
initialState: {
|
||||
count: 0
|
||||
}
|
||||
})
|
||||
|
||||
app.use(function (state, tx) {
|
||||
if(state.count === tx.nonce) {
|
||||
state.count++
|
||||
}
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
run `node app.js`, then:
|
||||
|
||||
```bash
|
||||
$ curl http://localhost:3000/state
|
||||
# { "count": 0 }
|
||||
|
||||
$ curl http://localhost:3000/txs -d '{ "nonce": 0 }'
|
||||
# { "ok": true }
|
||||
|
||||
$ curl http://localhost:3000/state
|
||||
# { "count": 1 }
|
||||
```
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1003,7 +1003,7 @@ where the processes that caused the consensus to fail (ie. caused clients of
|
|||
the protocol to accept different values - a fork) can be identified and punished
|
||||
according to the rules of the protocol, or, possibly, the legal system. When
|
||||
the legal system is unreliable or excessively expensive to invoke, validators can be forced to make security
|
||||
deposits in order to participate, and those deposits can be revoked, or slashed,
|
||||
deposits in order to participate, and those deposits can be jailed, or slashed,
|
||||
when malicious behaviour is detected [\[10\]][10].
|
||||
|
||||
Note this is unlike Bitcoin, where forking is a regular occurence due to
|
||||
|
|
|
@ -98,7 +98,7 @@ When you query an account balance with zero tokens, you will get this error: `No
|
|||
The following command could be used to send coins from one account to another:
|
||||
|
||||
```bash
|
||||
gaiacli send \
|
||||
gaiacli tx send \
|
||||
--amount=10faucetToken \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name> \
|
||||
|
@ -111,7 +111,7 @@ The `--amount` flag accepts the format `--amount=<value|coin_name>`.
|
|||
|
||||
::: tip Note
|
||||
You may want to cap the maximum gas that can be consumed by the transaction via the `--gas` flag.
|
||||
If set to 0, the gas limit will be automatically estimated.
|
||||
If you pass `--gas=simulate`, the gas limit will be automatically estimated.
|
||||
Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.0.
|
||||
:::
|
||||
|
||||
|
@ -131,7 +131,7 @@ gaiacli account <account_cosmos> --block=<block_height>
|
|||
You can simulate a transaction without actually broadcasting it by appending the `--dry-run` flag to the command line:
|
||||
|
||||
```bash
|
||||
gaiacli send \
|
||||
gaiacli tx send \
|
||||
--amount=10faucetToken \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name> \
|
||||
|
@ -142,7 +142,7 @@ gaiacli send \
|
|||
Furthermore, you can build a transaction and print its JSON format to STDOUT by appending `--generate-only` to the list of the command line arguments:
|
||||
|
||||
```bash
|
||||
gaiacli send \
|
||||
gaiacli tx send \
|
||||
--amount=10faucetToken \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name> \
|
||||
|
@ -153,7 +153,7 @@ gaiacli send \
|
|||
You can now sign the transaction file generated through the `--generate-only` flag by providing your key to the following command:
|
||||
|
||||
```bash
|
||||
gaiacli sign \
|
||||
gaiacli tx sign \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name>
|
||||
unsignedSendTx.json > signedSendTx.json
|
||||
|
@ -162,7 +162,7 @@ gaiacli sign \
|
|||
You can broadcast the signed transaction to a node by providing the JSON file to the following command:
|
||||
|
||||
```
|
||||
gaiacli broadcast --node=<node> signedSendTx.json
|
||||
gaiacli tx broadcast --node=<node> signedSendTx.json
|
||||
```
|
||||
|
||||
### Staking
|
||||
|
@ -180,13 +180,13 @@ On the upcoming mainnet, you can delegate `atom` to a validator. These [delegato
|
|||
You can query the list of all validators of a specific chain:
|
||||
|
||||
```bash
|
||||
gaiacli stake validators
|
||||
gaiacli query validators
|
||||
```
|
||||
|
||||
If you want to get the information of a single validator you can check it with:
|
||||
|
||||
```bash
|
||||
gaiacli stake validator <account_cosmosval>
|
||||
gaiacli query validator <account_cosmosval>
|
||||
```
|
||||
|
||||
#### Bond Tokens
|
||||
|
@ -194,7 +194,7 @@ gaiacli stake validator <account_cosmosval>
|
|||
On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond tokens to a testnet validator (*i.e.* delegate):
|
||||
|
||||
```bash
|
||||
gaiacli stake delegate \
|
||||
gaiacli tx delegate \
|
||||
--amount=10steak \
|
||||
--validator=$(gaiad tendermint show-validator) \
|
||||
--name=<key_name> \
|
||||
|
@ -212,7 +212,7 @@ Don't use more `steak` thank you have! You can always get more by using the [Fau
|
|||
Once submitted a delegation to a validator, you can see it's information by using the following command:
|
||||
|
||||
```bash
|
||||
gaiacli stake delegation \
|
||||
gaiacli query delegation \
|
||||
--address-delegator=<account_cosmos> \
|
||||
--validator=<account_cosmosval>
|
||||
```
|
||||
|
@ -220,7 +220,7 @@ gaiacli stake delegation \
|
|||
Or if you want to check all your current delegations with disctinct validators:
|
||||
|
||||
```bash
|
||||
gaiacli stake delegations <account_cosmos>
|
||||
gaiacli query delegations <account_cosmos>
|
||||
```
|
||||
|
||||
You can also get previous delegation(s) status by adding the `--height` flag.
|
||||
|
@ -230,17 +230,17 @@ You can also get previous delegation(s) status by adding the `--height` flag.
|
|||
If for any reason the validator misbehaves, or you just want to unbond a certain amount of tokens, use this following command. You can unbond a specific `shares-amount` (eg:`12.1`\) or a `shares-percent` (eg:`25`) with the corresponding flags.
|
||||
|
||||
```bash
|
||||
gaiacli stake unbond begin \
|
||||
gaiacli tx unbond begin \
|
||||
--validator=<account_cosmosval> \
|
||||
--shares-percent=100 \
|
||||
--from=<key_name> \
|
||||
--chain-id=<chain_id>
|
||||
```
|
||||
|
||||
Later you must complete the unbonding process by using the `gaiacli stake unbond complete` command:
|
||||
Later you must complete the unbonding process by using the `gaiacli tx unbond complete` command:
|
||||
|
||||
```bash
|
||||
gaiacli stake unbond complete \
|
||||
gaiacli tx unbond complete \
|
||||
--validator=<account_cosmosval> \
|
||||
--from=<key_name> \
|
||||
--chain-id=<chain_id>
|
||||
|
@ -251,7 +251,7 @@ gaiacli stake unbond complete \
|
|||
Once you begin an unbonding-delegation, you can see it's information by using the following command:
|
||||
|
||||
```bash
|
||||
gaiacli stake unbonding-delegation \
|
||||
gaiacli query unbonding-delegation \
|
||||
--address-delegator=<account_cosmos> \
|
||||
--validator=<account_cosmosval> \
|
||||
```
|
||||
|
@ -259,7 +259,7 @@ gaiacli stake unbonding-delegation \
|
|||
Or if you want to check all your current unbonding-delegations with disctinct validators:
|
||||
|
||||
```bash
|
||||
gaiacli stake unbonding-delegations <account_cosmos>
|
||||
gaiacli query unbonding-delegations <account_cosmos>
|
||||
```
|
||||
|
||||
You can also get previous unbonding-delegation(s) status by adding the `--height` flag.
|
||||
|
@ -269,7 +269,7 @@ You can also get previous unbonding-delegation(s) status by adding the `--height
|
|||
A redelegation is a type delegation that allows you to bond illiquid tokens from one validator to another:
|
||||
|
||||
```bash
|
||||
gaiacli stake redelegate begin \
|
||||
gaiacli tx redelegate begin \
|
||||
--address-validator-source=<account_cosmosval> \
|
||||
--address-validator-dest=<account_cosmosval> \
|
||||
--shares-percent=50 \
|
||||
|
@ -279,10 +279,10 @@ gaiacli stake redelegate begin \
|
|||
|
||||
Here you can also redelegate a specific `shares-amount` or a `shares-percent` with the corresponding flags.
|
||||
|
||||
Later you must complete the redelegation process by using the `gaiacli stake redelegate complete` command:
|
||||
Later you must complete the redelegation process by using the `gaiacli tx redelegate complete` command:
|
||||
|
||||
```bash
|
||||
gaiacli stake unbond complete \
|
||||
gaiacli tx unbond complete \
|
||||
--validator=<account_cosmosval> \
|
||||
--from=<key_name> \
|
||||
--chain-id=<chain_id>
|
||||
|
@ -293,7 +293,7 @@ gaiacli stake unbond complete \
|
|||
Once you begin an redelegation, you can see it's information by using the following command:
|
||||
|
||||
```bash
|
||||
gaiacli stake redelegation \
|
||||
gaiacli query redelegation \
|
||||
--address-delegator=<account_cosmos> \
|
||||
--address-validator-source=<account_cosmosval> \
|
||||
--address-validator-dest=<account_cosmosval> \
|
||||
|
@ -302,7 +302,7 @@ gaiacli stake redelegation \
|
|||
Or if you want to check all your current unbonding-delegations with disctinct validators:
|
||||
|
||||
```bash
|
||||
gaiacli stake redelegations <account_cosmos>
|
||||
gaiacli query redelegations <account_cosmos>
|
||||
```
|
||||
|
||||
You can also get previous redelegation(s) status by adding the `--height` flag.
|
||||
|
@ -331,7 +331,7 @@ In order to create a governance proposal, you must submit an initial deposit alo
|
|||
- `type`: Type of proposal. Must be of value _Text_ (types _SoftwareUpgrade_ and _ParameterChange_ not supported yet).
|
||||
|
||||
```bash
|
||||
gaiacli gov submit-proposal \
|
||||
gaiacli tx submit-proposal \
|
||||
--title=<title> \
|
||||
--description=<description> \
|
||||
--type=<Text/ParameterChange/SoftwareUpgrade> \
|
||||
|
@ -346,14 +346,14 @@ gaiacli gov submit-proposal \
|
|||
Once created, you can now query information of the proposal:
|
||||
|
||||
```bash
|
||||
gaiacli gov query-proposal \
|
||||
gaiacli query proposal \
|
||||
--proposal-id=<proposal_id>
|
||||
```
|
||||
|
||||
Or query all available proposals:
|
||||
|
||||
```bash
|
||||
gaiacli gov query-proposals
|
||||
gaiacli query proposals
|
||||
```
|
||||
|
||||
You can also query proposals filtered by `voter` or `depositer` by using the corresponding flags.
|
||||
|
@ -363,7 +363,7 @@ You can also query proposals filtered by `voter` or `depositer` by using the cor
|
|||
In order for a proposal to be broadcasted to the network, the amount deposited must be above a `minDeposit` value (default: `10 steak`). If the proposal you previously created didn't meet this requirement, you can still increase the total amount deposited to activate it. Once the minimum deposit is reached, the proposal enters voting period:
|
||||
|
||||
```bash
|
||||
gaiacli gov deposit \
|
||||
gaiacli tx deposit \
|
||||
--proposal-id=<proposal_id> \
|
||||
--depositer=<account_cosmos> \
|
||||
--deposit=<200steak> \
|
||||
|
@ -378,7 +378,7 @@ gaiacli gov deposit \
|
|||
After a proposal's deposit reaches the `MinDeposit` value, the voting period opens. Bonded `Atom` holders can then cast vote on it:
|
||||
|
||||
```bash
|
||||
gaiacli gov vote \
|
||||
gaiacli tx vote \
|
||||
--proposal-id=<proposal_id> \
|
||||
--voter=<account_cosmos> \
|
||||
--option=<Yes/No/NoWithVeto/Abstain> \
|
||||
|
@ -391,7 +391,7 @@ gaiacli gov vote \
|
|||
Check the vote with the option you just submitted:
|
||||
|
||||
```bash
|
||||
gaiacli gov query-vote \
|
||||
gaiacli query vote \
|
||||
--proposal-id=<proposal_id> \
|
||||
--voter=<account_cosmos>
|
||||
```
|
||||
|
@ -401,7 +401,7 @@ gaiacli gov query-vote \
|
|||
You can get the current parameters that define high level settings for staking:
|
||||
|
||||
```
|
||||
gaiacli stake parameters
|
||||
gaiacli query parameters
|
||||
```
|
||||
|
||||
With the above command you will get the values for:
|
||||
|
@ -420,7 +420,7 @@ All this values can be updated though a `governance` process by submitting a par
|
|||
A staking `Pool` defines the dynamic parameters of the current state. You can query them with the following command:
|
||||
|
||||
```
|
||||
gaiacli stake pool
|
||||
gaiacli query pool
|
||||
```
|
||||
|
||||
With the `pool` command you will get the values for:
|
||||
|
|
|
@ -139,8 +139,8 @@ Amino can also be used for persistent storage of interfaces.
|
|||
To use Amino, simply create a codec, and then register types:
|
||||
|
||||
```
|
||||
func NewCodec() *wire.Codec {
|
||||
cdc := wire.NewCodec()
|
||||
func NewCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
|
||||
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
|
||||
|
@ -175,7 +175,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg {
|
|||
}
|
||||
|
||||
// Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue
|
||||
func tx2Decoder(cdc *wire.Codec) sdk.TxDecoder {
|
||||
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx app2Tx
|
||||
err := cdc.UnmarshalBinary(txBytes, &tx)
|
||||
|
|
|
@ -285,7 +285,7 @@ it can't increment sequence numbers, change PubKeys, or otherwise.
|
|||
A `bank.Keeper` is easily instantiated from an `AccountMapper`:
|
||||
|
||||
```go
|
||||
bankKeeper = bank.NewKeeper(accountMapper)
|
||||
bankKeeper = bank.NewBaseKeeper(accountMapper)
|
||||
```
|
||||
|
||||
We can then use it within a handler, instead of working directly with the
|
||||
|
@ -336,7 +336,7 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
|
||||
// Set various mappers/keepers to interact easily with underlying stores
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAccount, auth.ProtoBaseAccount)
|
||||
bankKeeper := bank.NewKeeper(accountMapper)
|
||||
bankKeeper := bank.NewBaseKeeper(accountMapper)
|
||||
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
|
||||
|
||||
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
|
||||
|
|
|
@ -57,6 +57,7 @@ func NewMsgSend(from, to sdk.AccAddress, amt sdk.Coins) MsgSend {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgSend) Type() string { return "send" }
|
||||
func (msg MsgSend) Name() string { return "send" }
|
||||
|
||||
// Implements Msg. Ensure the addresses are good and the
|
||||
// amount is positive.
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
bapp "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -25,8 +25,8 @@ var (
|
|||
issuer = ed25519.GenPrivKey().PubKey().Address()
|
||||
)
|
||||
|
||||
func NewCodec() *wire.Codec {
|
||||
cdc := wire.NewCodec()
|
||||
func NewCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
|
||||
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
|
||||
|
@ -77,6 +77,7 @@ type MsgIssue struct {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgIssue) Type() string { return "issue" }
|
||||
func (msg MsgIssue) Name() string { return "issue" }
|
||||
|
||||
// Implements Msg. Ensures addresses are valid and Coin is positive
|
||||
func (msg MsgIssue) ValidateBasic() sdk.Error {
|
||||
|
@ -196,7 +197,7 @@ func (tx app2Tx) GetSignature() []byte {
|
|||
}
|
||||
|
||||
// Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue
|
||||
func tx2Decoder(cdc *wire.Codec) sdk.TxDecoder {
|
||||
func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx app2Tx
|
||||
err := cdc.UnmarshalBinary(txBytes, &tx)
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
bapp "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
@ -31,7 +31,7 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
|
||||
// Set various mappers/keepers to interact easily with underlying stores
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAccount, auth.ProtoBaseAccount)
|
||||
bankKeeper := bank.NewKeeper(accountMapper)
|
||||
bankKeeper := bank.NewBaseKeeper(accountMapper)
|
||||
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
|
||||
|
||||
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
|
||||
|
@ -51,12 +51,12 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
}
|
||||
|
||||
// Update codec from app2 to register imported modules
|
||||
func UpdatedCodec() *wire.Codec {
|
||||
cdc := wire.NewCodec()
|
||||
func UpdatedCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
|
||||
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
|
||||
auth.RegisterWire(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
return cdc
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
bapp "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
@ -29,7 +29,7 @@ func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
|
||||
// Set various mappers/keepers to interact easily with underlying stores
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAccount, auth.ProtoBaseAccount)
|
||||
bankKeeper := bank.NewKeeper(accountMapper)
|
||||
bankKeeper := bank.NewBaseKeeper(accountMapper)
|
||||
|
||||
// TODO
|
||||
keyFees := sdk.NewKVStoreKey("fee")
|
||||
|
@ -76,7 +76,7 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount, err error) {
|
|||
|
||||
// InitChainer will set initial balances for accounts as well as initial coin metadata
|
||||
// MsgIssue can no longer be used to create new coin
|
||||
func NewInitChainer(cdc *wire.Codec, accountMapper auth.AccountMapper) sdk.InitChainer {
|
||||
func NewInitChainer(cdc *codec.Codec, accountMapper auth.AccountMapper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules.
|
||||
|
||||
```go
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
wire.RegisterCrypto(cdc) // Register crypto.
|
||||
sdk.RegisterWire(cdc) // Register Msgs
|
||||
bank.RegisterWire(cdc)
|
||||
simplestake.RegisterWire(cdc)
|
||||
simpleGov.RegisterWire(cdc)
|
||||
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)
|
||||
|
|
|
@ -33,7 +33,7 @@ var cdc = MakeCodec()
|
|||
- 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.NewKeeper(app.accountMapper)
|
||||
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))
|
||||
```
|
||||
|
|
|
@ -13,7 +13,7 @@ var SimpleGovAppInit = server.AppInit{
|
|||
}
|
||||
|
||||
// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state
|
||||
func SimpleGovAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -28,7 +28,7 @@ Then, let us define the structure of our application.
|
|||
// Extended ABCI application
|
||||
type SimpleGovApp struct {
|
||||
*bam.BaseApp
|
||||
cdc *wire.Codec
|
||||
cdc *codec.Codec
|
||||
|
||||
// keys to access the substores
|
||||
capKeyMainStore *sdk.KVStoreKey
|
||||
|
|
|
@ -28,7 +28,7 @@ Before getting in the bulk of the code, we will start by some introductory conte
|
|||
+ [Types](module-types.md)
|
||||
+ [Keeper](module-keeper.md)
|
||||
+ [Handler](module-handler.md)
|
||||
+ [Wire](module-wire.md)
|
||||
+ [Wire](module-codec.md)
|
||||
+ [Errors](module-errors.md)
|
||||
+ Command-Line Interface and Rest API
|
||||
* [Command-Line Interface](module-cli.md)
|
||||
|
|
|
@ -14,7 +14,7 @@ The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the sc
|
|||
)
|
||||
|
||||
// Main command function. One function for each command.
|
||||
func Command(codec *wire.Codec) *cobra.Command {
|
||||
func Command(codec *codec.Codec) *cobra.Command {
|
||||
// Create the command to return
|
||||
command := &cobra.Command{
|
||||
Use: "actual command",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
## 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).
|
|
@ -7,7 +7,7 @@ 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 wire.go
|
||||
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:
|
||||
|
@ -25,7 +25,7 @@ x
|
|||
├─── keeper_keys.go
|
||||
├─── keeper.go
|
||||
├─── types.go
|
||||
└─── wire.go
|
||||
└─── codec.go
|
||||
```
|
||||
|
||||
Let us go into the detail of each of these files.
|
|
@ -47,7 +47,7 @@ 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 *wire.Codec // Codec to encore/decode structs
|
||||
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
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
## Wire
|
||||
|
||||
**File: [`x/simple_governance/wire.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/wire.go)**
|
||||
|
||||
The `wire.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 RegisterWire(cdc *wire.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).
|
|
@ -0,0 +1,25 @@
|
|||
WORK IN PROGRESS
|
||||
See PR comments here https://github.com/cosmos/cosmos-sdk/pull/2072
|
||||
|
||||
# Keeper
|
||||
|
||||
## Denom Metadata
|
||||
|
||||
The BankKeeper contains a store that stores the metadata of different token denoms. Denoms are referred to by their name, same as the `denom` field in sdk.Coin. The different attributes of a denom are stored in the denom metadata store under the key `[denom name]:[attribute name]`. The default attributes in the store are explained below. However, this can be extended by the developer or through SoftwareUpgrade proposals.
|
||||
|
||||
### Decimals `int8`
|
||||
|
||||
- `Base Unit` = The common standard for the default "standard" size of a token. Examples: 1 Bitcoin or 1 Ether.
|
||||
- `Smallest Unit` = The smallest possible denomination of a token. A fraction of the base unit. Examples: 1 satoshi or 1 wei.
|
||||
|
||||
All amounts throughout the SDK are denominated in the smallest unit of a token, so that all amounts can be expressed as integers. However, UIs typically want to display token values in the base unit, so the Decimals metadata field standardizes the number of digits that come after the decimal place in the base unit.
|
||||
|
||||
`1 [Base Unit] = 10^(N) [Smallest Unit]`
|
||||
|
||||
### TotalSupply `sdk.Integer`
|
||||
|
||||
The TotalSupply of a denom is the total amount of a token that exists (known to the chain) across all accounts and modules. It is denominated in the `smallest unit` of a denom. It can be changed by the Keeper functions `MintCoins` and `BurnCoins`. `AddCoins` and `SubtractCoins` are used when adding or subtracting coins for an account, but not removing them from total supply (for example, when moving the coins to the control of the staking module).
|
||||
|
||||
### Aliases `[]string`
|
||||
|
||||
Aliases is an array of strings that are "alternative names" for a token. As an example, while the Ether's denom name might be `ether`, a possible alias could be `ETH`. This field can be useful for UIs and clients. It is intended that this field can be modified by a governance mechanism.
|
|
@ -1,2 +0,0 @@
|
|||
- SDK related specifications (ie. how multistore, signatures, etc. work).
|
||||
- Basecoin (SendTx)
|
|
@ -13,13 +13,13 @@ has to be created and the previous one rendered inactive.
|
|||
```go
|
||||
type DepositProcedure struct {
|
||||
MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period.
|
||||
MaxDepositPeriod int64 // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||
MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type VotingProcedure struct {
|
||||
VotingPeriod int64 // Length of the voting period. Initial value: 2 weeks
|
||||
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -28,7 +28,6 @@ type TallyingProcedure struct {
|
|||
Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5
|
||||
Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||
GovernancePenalty sdk.Dec // Penalty if validator does not vote
|
||||
GracePeriod int64 // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -97,10 +96,10 @@ type Proposal struct {
|
|||
Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
|
||||
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
|
||||
Deposits []Deposit // List of deposits on the proposal
|
||||
SubmitBlock int64 // Height of the block where TxGovSubmitProposal was included
|
||||
SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included
|
||||
Submitter sdk.Address // Address of the submitter
|
||||
|
||||
VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
|
||||
VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached
|
||||
CurrentStatus ProposalStatus // Current status of the proposal
|
||||
|
||||
YesVotes sdk.Dec
|
||||
|
@ -137,7 +136,7 @@ For pseudocode purposes, here are the two function we will use to read or write
|
|||
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
|
||||
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest
|
||||
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if
|
||||
`CurrentBlock == VotingStartBlock + activeProcedure.VotingPeriod`. If it is,
|
||||
`CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is,
|
||||
then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted
|
||||
and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded.
|
||||
After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated.
|
||||
|
@ -159,7 +158,7 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
|||
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
|
||||
votingProcedure = load(GlobalParams, 'VotingProcedure')
|
||||
|
||||
if (CurrentBlock == proposal.VotingStartBlock + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive)
|
||||
if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive)
|
||||
|
||||
// End of voting period, tally
|
||||
|
||||
|
@ -192,14 +191,10 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
|||
|
||||
tallyingProcedure = load(GlobalParams, 'TallyingProcedure')
|
||||
|
||||
// Slash validators that did not vote, or update tally if they voted
|
||||
// Update tally if validator voted they voted
|
||||
for each validator in validators
|
||||
if (validator.bondHeight < CurrentBlock - tallyingProcedure.GracePeriod)
|
||||
// only slash if validator entered validator set before grace period
|
||||
if (!tmpValMap(validator).HasVoted)
|
||||
slash validator by tallyingProcedure.GovernancePenalty
|
||||
else
|
||||
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
|
||||
if tmpValMap(validator).HasVoted
|
||||
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ The specification has focused on semantics and functionality of the IBC protocol
|
|||
|
||||
In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: Ethereum's RLP[[6](./references.md#6)] and Google's Protobuf[[7](./references.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures.
|
||||
|
||||
The tendermint-specific data structures are encoded with go-wire[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner.
|
||||
The tendermint-specific data structures are encoded with go-amino[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner.
|
||||
|
||||
For the following appendixes, the data structure specifications will be in proto3[[9](./references.md#9)] format.
|
||||
|
||||
|
@ -61,7 +61,7 @@ The IBC protocol does not handle these kinds of errors. They must be handled ind
|
|||
|
||||
**TODO: clean this all up**
|
||||
|
||||
This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team.
|
||||
This is a mess now, we need to figure out what formats we use, define go-amino, etc. or just point to the source???? Will do more later, need help here from the tendermint core team.
|
||||
|
||||
In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks.
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ Every transaction on the same chain already has a well-defined causality relatio
|
|||
|
||||
For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain `B` when a particular IBC packet is committed to chain `B`, and require outgoing sends of that packet on chain `A` to escrow an equal amount of the asset on chain `A` until the vouchers are later redeemed back to chain `A` with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain `B` can later be redeemed back to chain `A`.
|
||||
|
||||
This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-formats).
|
||||
This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement amino-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-formats).
|
||||
|
||||
### 3.2 Definitions
|
||||
|
||||
|
|
|
@ -6,4 +6,4 @@ We have demonstrated a secure, performant, and flexible protocol for cross-block
|
|||
|
||||
This document defines solely a message queue protocol - not the application-level semantics which must sit on top of it to enable asset transfer between two chains. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC.
|
||||
|
||||
There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in Golang and released under the Apache license. To facilitate implementations in other langauages which are wire-compatible with the Cosmos implementation, the following appendices define exact message and binary encoding formats.
|
||||
There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in Golang and released under the Apache license. To facilitate implementations in other langauages which are amino-compatible with the Cosmos implementation, the following appendices define exact message and binary encoding formats.
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
[https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/)
|
||||
|
||||
##### 8:
|
||||
[https://github.com/tendermint/go-wire](https://github.com/tendermint/go-wire)
|
||||
[https://github.com/tendermint/go-amino](https://github.com/tendermint/go-amino)
|
||||
|
||||
##### 9:
|
||||
[https://developers.google.com/protocol-buffers/docs/proto3](https://developers.google.com/protocol-buffers/docs/proto3)
|
||||
|
|
|
@ -12,7 +12,7 @@ the changes cleared
|
|||
|
||||
```golang
|
||||
EndBlock() ValidatorSetChanges
|
||||
vsc = GetTendermintUpdates()
|
||||
vsc = GetValidTendermintUpdates()
|
||||
ClearTendermintUpdates()
|
||||
return vsc
|
||||
```
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
## Receiver Hooks
|
||||
|
||||
The staking module allow for the following hooks to be registered with staking events:
|
||||
|
||||
``` golang
|
||||
// event hooks for staking validator object
|
||||
type StakingHooks interface {
|
||||
OnValidatorCreated(ctx Context, address ValAddress) // called when a validator is created
|
||||
OnValidatorCommissionChange(ctx Context, address ValAddress) // called when a validator's commission is modified
|
||||
OnValidatorRemoved(ctx Context, address ValAddress) // called when a validator is deleted
|
||||
|
||||
OnValidatorBonded(ctx Context, address ConsAddress) // called when a validator is bonded
|
||||
OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // called when a validator begins unbonding
|
||||
|
||||
OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is created
|
||||
OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation's shares are modified
|
||||
OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // called when a delegation is removed
|
||||
}
|
||||
```
|
|
@ -43,12 +43,13 @@ type Params struct {
|
|||
Validators are identified according to the `OperatorAddr`, an SDK validator
|
||||
address for the operator of the validator.
|
||||
|
||||
Validators also have a `ConsPubKey`, the public key of the validator.
|
||||
|
||||
Validators are indexed in the store using the following maps:
|
||||
Validators also have a `ConsPubKey`, the public key of the validator used in
|
||||
Tendermint consensus. The validator can be retrieved from it's `ConsPubKey`
|
||||
once it can be converted into the corresponding `ConsAddr`. Validators are
|
||||
indexed in the store using the following maps:
|
||||
|
||||
- Validators: `0x02 | OperatorAddr -> amino(validator)`
|
||||
- ValidatorsByPubKey: `0x03 | ConsPubKey -> OperatorAddr`
|
||||
- ValidatorsByConsAddr: `0x03 | ConsAddr -> OperatorAddr`
|
||||
- ValidatorsByPower: `0x05 | power | blockHeight | blockTx -> OperatorAddr`
|
||||
|
||||
`Validators` is the primary index - it ensures that each operator can have only one
|
||||
|
@ -69,7 +70,7 @@ validator.
|
|||
|
||||
```golang
|
||||
type Validator struct {
|
||||
ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator
|
||||
ConsPubKey crypto.PubKey // Tendermint consensus pubkey of validator
|
||||
Jailed bool // has the validator been jailed?
|
||||
|
||||
Status sdk.BondStatus // validator status (bonded/unbonding/unbonded)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue