Release v0.33.0

This commit is contained in:
Jack Zampolin 2019-03-07 09:47:59 -08:00 committed by GitHub
commit 7b4104aced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
261 changed files with 4936 additions and 4325 deletions

View File

@ -326,13 +326,24 @@ jobs:
GAIAD_VERSION="stable"
elif [ "${CIRCLE_BRANCH}" == "develop" ]; then
GAIAD_VERSION="develop"
else
GAIAD_VERSION=`/tmp/workspace/bin/gaiad version`
fi
docker build -t tendermint/gaia:$GAIAD_VERSION .
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker push tendermint/gaia:$GAIAD_VERSION
docker_tagged:
<<: *linux_defaults
steps:
- attach_workspace:
at: /tmp/workspace
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run: |
docker build -t tendermint/gaia:$CIRCLE_TAG .
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker push tendermint/gaia:$CIRCLE_TAG
workflows:
version: 2
test-suite:
@ -345,6 +356,16 @@ workflows:
- develop
requires:
- setup_dependencies
- docker_tagged:
filters:
tags:
only:
- /^v.*/
branches:
ignore:
- /.*/
requires:
- setup_dependencies
- macos_ci:
filters:
branches:

View File

@ -1,5 +1,112 @@
# Changelog
## 0.33.0
BREAKING CHANGES
* Gaia REST API
* [\#3641](https://github.com/cosmos/cosmos-sdk/pull/3641) Remove the ability to use a Keybase from the REST API client:
* `password` and `generate_only` have been removed from the `base_req` object
* All txs that used to sign or use the Keybase now only generate the tx
* `keys` routes completely removed
* [\#3692](https://github.com/cosmos/cosmos-sdk/pull/3692) Update tx encoding and broadcasting endpoints:
* Remove duplicate broadcasting endpoints in favor of POST @ `/txs`
* The `Tx` field now accepts a `StdTx` and not raw tx bytes
* Move encoding endpoint to `/txs/encode`
* Gaia
* [\#3787](https://github.com/cosmos/cosmos-sdk/pull/3787) Fork the `x/bank` module into the Gaia application with only a
modified message handler, where the modified message handler behaves the same as
the standard `x/bank` message handler except for `MsgMultiSend` that must burn
exactly 9 atoms and transfer 1 atom, and `MsgSend` is disabled.
* [\#3789](https://github.com/cosmos/cosmos-sdk/pull/3789) Update validator creation flow:
* Remove `NewMsgCreateValidatorOnBehalfOf` and corresponding business logic
* Ensure the validator address equals the delegator address during
`MsgCreateValidator#ValidateBasic`
* SDK
* [\#3750](https://github.com/cosmos/cosmos-sdk/issues/3750) Track outstanding rewards per-validator instead of globally,
and fix the main simulation issue, which was that slashes of
re-delegations to a validator were not correctly accounted for
in fee distribution when the redelegation in question had itself
been slashed (from a fault committed by a different validator)
in the same BeginBlock. Outstanding rewards are now available
on a per-validator basis in REST.
* [\#3669](https://github.com/cosmos/cosmos-sdk/pull/3669) Ensure consistency in message naming, codec registration, and JSON
tags.
* [\#3788](https://github.com/cosmos/cosmos-sdk/pull/3788) Change order of operations for greater accuracy when calculating delegation share token value
* [\#3788](https://github.com/cosmos/cosmos-sdk/pull/3788) DecCoins.Cap -> DecCoins.Intersect
* [\#3666](https://github.com/cosmos/cosmos-sdk/pull/3666) Improve coins denom validation.
* [\#3751](https://github.com/cosmos/cosmos-sdk/pull/3751) Disable (temporarily) support for ED25519 account key pairs.
* Tendermint
* [\#3804] Update to Tendermint `v0.31.0-dev0`
FEATURES
* SDK
* [\#3719](https://github.com/cosmos/cosmos-sdk/issues/3719) DBBackend can now be set at compile time.
Defaults: goleveldb. Supported: cleveldb.
IMPROVEMENTS
* Gaia REST API
* Update the `TxResponse` type allowing for the `Logs` result to be JSON decoded automatically.
* Gaia CLI
* [\#3653](https://github.com/cosmos/cosmos-sdk/pull/3653) Prompt user confirmation prior to signing and broadcasting a transaction.
* [\#3670](https://github.com/cosmos/cosmos-sdk/pull/3670) CLI support for showing bech32 addresses in Ledger devices
* [\#3711](https://github.com/cosmos/cosmos-sdk/pull/3711) Update `tx sign` to use `--from` instead of the deprecated `--name`
CLI flag.
* [\#3738](https://github.com/cosmos/cosmos-sdk/pull/3738) Improve multisig UX:
* `gaiacli keys show -o json` now includes constituent pubkeys, respective weights and threshold
* `gaiacli keys show --show-multisig` now displays constituent pubkeys, respective weights and threshold
* `gaiacli tx sign --validate-signatures` now displays multisig signers with their respective weights
* [\#3730](https://github.com/cosmos/cosmos-sdk/issues/3730) Improve workflow for
`gaiad gentx` with offline public keys, by outputting stdtx file that needs to be signed.
* [\#3761](https://github.com/cosmos/cosmos-sdk/issues/3761) Querying account related information using custom querier in auth module
* SDK
* [\#3753](https://github.com/cosmos/cosmos-sdk/issues/3753) Remove no-longer-used governance penalty parameter
* [\#3679](https://github.com/cosmos/cosmos-sdk/issues/3679) Consistent operators across Coins, DecCoins, Int, Dec
replaced: Minus->Sub Plus->Add Div->Quo
* [\#3665](https://github.com/cosmos/cosmos-sdk/pull/3665) Overhaul sdk.Uint type in preparation for Coins Int -> Uint migration.
* [\#3691](https://github.com/cosmos/cosmos-sdk/issues/3691) Cleanup error messages
* [\#3456](https://github.com/cosmos/cosmos-sdk/issues/3456) Integrate in the Int.ToDec() convenience function
* [\#3300](https://github.com/cosmos/cosmos-sdk/pull/3300) Update the spec-spec, spec file reorg, and TOC updates.
* [\#3694](https://github.com/cosmos/cosmos-sdk/pull/3694) Push tagged docker images on docker hub when tag is created.
* [\#3716](https://github.com/cosmos/cosmos-sdk/pull/3716) Update file permissions the client keys directory and contents to `0700`.
* [\#3681](https://github.com/cosmos/cosmos-sdk/issues/3681) Migrate ledger-cosmos-go from ZondaX to Cosmos organization
* Tendermint
* [\#3699](https://github.com/cosmos/cosmos-sdk/pull/3699) Upgrade to Tendermint 0.30.1
BUG FIXES
* Gaia CLI
* [\#3731](https://github.com/cosmos/cosmos-sdk/pull/3731) `keys add --interactive` bip32 passphrase regression fix
* [\#3714](https://github.com/cosmos/cosmos-sdk/issues/3714) Fix USB raw access issues with gaiacli when installed via snap
* Gaia
* [\#3777](https://github.com/cosmso/cosmos-sdk/pull/3777) `gaiad export` no longer panics when the database is empty
* [\#3806](https://github.com/cosmos/cosmos-sdk/pull/3806) Properly return errors from a couple of struct Unmarshal functions
* SDK
* [\#3728](https://github.com/cosmos/cosmos-sdk/issues/3728) Truncate decimal multiplication & division in distribution to ensure
no more than the collected fees / inflation are distributed
* [\#3727](https://github.com/cosmos/cosmos-sdk/issues/3727) Return on zero-length (including []byte{}) PrefixEndBytes() calls
* [\#3559](https://github.com/cosmos/cosmos-sdk/issues/3559) fix occasional failing due to non-determinism in lcd test TestBonding
where validator is unexpectedly slashed throwing off test calculations
* [\#3411](https://github.com/cosmos/cosmos-sdk/pull/3411) Include the `RequestInitChain.Time` in the block header init during
`InitChain`.
* [\#3717](https://github.com/cosmos/cosmos-sdk/pull/3717) Update the vesting specification and implementation to cap deduction from
`DelegatedVesting` by at most `DelegatedVesting`. This accounts for the case where
the undelegation amount may exceed the original delegation amount due to
truncation of undelegation tokens.
* [\#3717](https://github.com/cosmos/cosmos-sdk/pull/3717) Ignore unknown proposers in allocating rewards for proposers, in case
unbonding period was just 1 block and proposer was already deleted.
* [\#3726](https://github.com/cosmos/cosmos-sdk/pull/3724) Cap(clip) reward to remaining coins in AllocateTokens.
## 0.32.0
BREAKING CHANGES
@ -18,19 +125,19 @@ BREAKING CHANGES
IMPROVEMENTS
* SDK
* [\#3311] Reconcile the `DecCoin/s` API with the `Coin/s` API.
* [\#3614] Add coin denom length checks to the coins constructors.
* [\#3311](https://github.com/cosmos/cosmos-sdk/pull/3311) Reconcile the `DecCoin/s` API with the `Coin/s` API.
* [\#3614](https://github.com/cosmos/cosmos-sdk/pull/3614) Add coin denom length checks to the coins constructors.
* [\#3621](https://github.com/cosmos/cosmos-sdk/issues/3621) remove many inter-module dependancies
* [\#3601] JSON-stringify the ABCI log response which includes the log and message
* [\#3601](https://github.com/cosmos/cosmos-sdk/pull/3601) JSON-stringify the ABCI log response which includes the log and message
index.
* [\#3604] Improve SDK funds related error messages and allow for unicode in
* [\#3604](https://github.com/cosmos/cosmos-sdk/pull/3604) Improve SDK funds related error messages and allow for unicode in
JSON ABCI log.
* [\#3620](https://github.com/cosmos/cosmos-sdk/pull/3620) Version command shows build tags
* [\#3638] Add Bcrypt benchmarks & justification of security parameter choice
* [\#3648] Add JSON struct tags to vesting accounts.
* [\#3638](https://github.com/cosmos/cosmos-sdk/pull/3638) Add Bcrypt benchmarks & justification of security parameter choice
* [\#3648](https://github.com/cosmos/cosmos-sdk/pull/3648) Add JSON struct tags to vesting accounts.
* Tendermint
* [\#3618] Upgrade to Tendermint 0.30.03
* [\#3618](https://github.com/cosmos/cosmos-sdk/pull/3618) Upgrade to Tendermint 0.30.03
BUG FIXES

67
Gopkg.lock generated
View File

@ -46,6 +46,14 @@
pruneopts = "UT"
revision = "52158e4697b87de16ed390e1bdaf813e581008fa"
[[projects]]
digest = "1:a2b34d1436ac24a0db176f84c015d80438602eeec12f2f8358bb72749711ab52"
name = "github.com/cosmos/ledger-cosmos-go"
packages = ["."]
pruneopts = "UT"
revision = "e2f595b3b7b222e1cbe9daf89e73e4dcab02d54c"
version = "v0.9.8"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
@ -59,8 +67,8 @@
name = "github.com/ethereum/go-ethereum"
packages = ["crypto/secp256k1"]
pruneopts = "UT"
revision = "7fa3509e2eaf1a4ebc12344590e5699406690f15"
version = "v1.8.22"
revision = "c942700427557e3ff6de3aaf6b916e2f056c1ec2"
version = "v1.8.23"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
@ -132,12 +140,12 @@
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
digest = "1:e4f5819333ac698d294fe04dbf640f84719658d5c7ce195b10060cc37292ce79"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = "UT"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a"
version = "v0.0.1"
[[projects]]
digest = "1:ca59b1175189b3f0e9f1793d2c350114be36eaabbe5b9f554b35edee1de50aea"
@ -183,12 +191,12 @@
version = "v1.0"
[[projects]]
branch = "master"
digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214"
digest = "1:a74b5a8e34ee5843cd6e65f698f3e75614f812ff170c2243425d75bc091e9af2"
name = "github.com/jmhodges/levigo"
packages = ["."]
pruneopts = "UT"
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
revision = "853d788c5c416eaaee5b044570784a96c7a26975"
version = "v1.0.0"
[[projects]]
branch = "master"
@ -207,12 +215,12 @@
version = "v1.8.0"
[[projects]]
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
digest = "1:3bb9c8451d199650bfd303e0068d86f135952fead374ad87c09a9b8a2cc4bd7c"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
version = "v0.0.4"
revision = "369ecd8cea9851e459abb67eb171853e3986591e"
version = "v0.0.6"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
@ -295,16 +303,17 @@
[[projects]]
branch = "master"
digest = "1:c65f369bae3dff3a0382e38f3fe4f62cdfecba59cb6429ee323b75afdd4f3ba3"
digest = "1:bfc66a1fd78dc8f14d419fd52a5a42db1d572e887ebcc80b98c1a515a8f2d837"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"iostats",
"nfs",
"xfs",
]
pruneopts = "UT"
revision = "de1b801bf34b80cd00f14087dc5a994bfe0296bc"
revision = "bbced9601137e764853b2fad7ec3e2dc4c504e02"
[[projects]]
digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64"
@ -357,12 +366,12 @@
version = "v0.0.3"
[[projects]]
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
digest = "1:1b753ec16506f5864d26a28b43703c58831255059644351bbcb019b843950900"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
version = "v1.0.0"
revision = "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1"
version = "v1.1.0"
[[projects]]
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
@ -428,15 +437,15 @@
version = "v0.14.1"
[[projects]]
digest = "1:e1cc8dd891e64aab63b0c09f2f12456cbe2cd9cbd9d96dfae3281245f05c2428"
digest = "1:1bb088f6291e5426e3874a60bca0e481a91a5633395d7e0c427ec3e49b626e7b"
name = "github.com/tendermint/iavl"
packages = ["."]
pruneopts = "UT"
revision = "de0740903a67b624d887f9055d4c60175dcfa758"
version = "v0.12.0"
revision = "ac7c35c12e8633a1e9fd0b52a00b900b40f32cd3"
version = "v0.12.1"
[[projects]]
digest = "1:89f6fe8d02b427996828fbf43720ed1297a2e92c930b98dd302767b5ad796579"
digest = "1:088088db7ae1174ce66abecbca6924e969cd05632053532ed7dadc7cc35f7885"
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
@ -502,7 +511,7 @@
"version",
]
pruneopts = "UT"
revision = "v0.30.0"
revision = "v0.31.0-dev0"
[[projects]]
digest = "1:b73f5e117bc7c6e8fc47128f20db48a873324ad5cfeeebfc505e85c58682b5e4"
@ -512,14 +521,6 @@
revision = "302fd402163c34626286195dfa9adac758334acc"
version = "v0.9.0"
[[projects]]
digest = "1:fca24169988a61ea725d1326de30910d8049fe68bcbc194d28803f9a76dda380"
name = "github.com/zondax/ledger-cosmos-go"
packages = ["."]
pruneopts = "UT"
revision = "69fdb8ce5e5b9d9c3b22b9248e117b231d4f06dd"
version = "v0.9.7"
[[projects]]
digest = "1:f8e4c0b959174a1fa5946b12f1f2ac7ea5651bef20a9e4a8dac55dbffcaa6cd6"
name = "github.com/zondax/ledger-go"
@ -611,7 +612,7 @@
revision = "383e8b2c3b9e36c4076b235b32537292176bae20"
[[projects]]
digest = "1:9ab5a33d8cb5c120602a34d2e985ce17956a4e8c2edce7e6961568f95e40c09a"
digest = "1:cbc746de4662c66fd24a037501bd65aa0f8ad0bfca0c92576e0abb88864e3741"
name = "google.golang.org/grpc"
packages = [
".",
@ -647,8 +648,8 @@
"tap",
]
pruneopts = "UT"
revision = "a02b0774206b209466313a0b525d2c738fe407eb"
version = "v1.18.0"
revision = "2fdaae294f38ed9a121193c51ec99fecd3b13eb7"
version = "v1.19.0"
[[projects]]
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
@ -666,6 +667,7 @@
"github.com/bgentry/speakeasy",
"github.com/btcsuite/btcd/btcec",
"github.com/cosmos/go-bip39",
"github.com/cosmos/ledger-cosmos-go",
"github.com/gogo/protobuf/proto",
"github.com/golang/protobuf/proto",
"github.com/gorilla/mux",
@ -717,7 +719,6 @@
"github.com/tendermint/tendermint/types",
"github.com/tendermint/tendermint/types/time",
"github.com/tendermint/tendermint/version",
"github.com/zondax/ledger-cosmos-go",
"golang.org/x/crypto/bcrypt",
]
solver-name = "gps-cdcl"

View File

@ -36,15 +36,15 @@
[[override]]
name = "github.com/tendermint/iavl"
version = "v0.12.0"
version = "~v0.12.0"
[[override]]
name = "github.com/tendermint/tendermint"
revision = "v0.30.0"
revision = "v0.31.0-dev0"
[[constraint]]
name = "github.com/zondax/ledger-cosmos-go"
version = "=v0.9.7"
name = "github.com/cosmos/ledger-cosmos-go"
version = "=v0.9.8"
## deps without releases:

View File

@ -2,13 +2,7 @@ PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation')
PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation')
VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//')
COMMIT := $(shell git log -1 --format='%H')
BUILD_TAGS = netgo
CAT := $(if $(filter $(OS),Windows_NT),type,cat)
BUILD_FLAGS = -tags "$(BUILD_TAGS)" -ldflags \
'-X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
-X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \
-X github.com/cosmos/cosmos-sdk/version.VendorDirHash=$(shell $(CAT) vendor-deps) \
-X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(BUILD_TAGS)"'
LEDGER_ENABLED ?= true
GOTOOLS = \
github.com/golang/dep/cmd/dep \
@ -16,6 +10,53 @@ GOTOOLS = \
github.com/rakyll/statik
GOBIN ?= $(GOPATH)/bin
# process build tags
build_tags = netgo
ifeq ($(LEDGER_ENABLED),true)
ifeq ($(OS),Windows_NT)
GCCEXE = $(shell where gcc.exe 2> NUL)
ifeq ($(GCCEXE),)
$(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false)
else
build_tags += ledger
endif
else
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S),OpenBSD)
$(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988))
else
GCC = $(shell command -v gcc 2> /dev/null)
ifeq ($(GCC),)
$(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false)
else
build_tags += ledger
endif
endif
endif
endif
ifeq ($(WITH_CLEVELDB),yes)
build_tags += gcc
endif
build_tags += $(BUILD_TAGS)
build_tags := $(strip $(build_tags))
# process linker flags
ldflags = -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
-X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \
-X github.com/cosmos/cosmos-sdk/version.VendorDirHash=$(shell $(CAT) vendor-deps) \
-X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags)"
ifeq ($(WITH_CLEVELDB),yes)
ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb
endif
ldflags += $(LDFLAGS)
ldflags := $(strip $(ldflags))
BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)'
all: devtools vendor-deps install test_lint test
# The below include contains the tools target.
@ -29,29 +70,6 @@ ci: devtools vendor-deps install test_cover test_lint test
########################################
### Build/Install
ifeq ($(LEDGER_ENABLED),true)
ifeq ($(OS),Windows_NT)
GCCEXE = $(shell where gcc.exe 2> NUL)
ifeq ($(GCCEXE),)
$(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false)
else
BUILD_TAGS += ledger
endif
else
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S),OpenBSD)
$(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988))
else
GCC = $(shell command -v gcc 2> /dev/null)
ifeq ($(GCC),)
$(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false)
else
BUILD_TAGS += ledger
endif
endif
endif
endif
build:
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad

View File

@ -1,52 +1,57 @@
## PENDING
# PENDING CHANGELOG
BREAKING CHANGES
<!----------------------------- BREAKING CHANGES ----------------------------->
* Gaia REST API
## BREAKING CHANGES
* Gaia CLI
### Gaia REST API
* Gaia
### Gaia CLI
* SDK
### Gaia
* Tendermint
### SDK
### Tendermint
FEATURES
<!--------------------------------- FEATURES --------------------------------->
* Gaia REST API
## FEATURES
* Gaia CLI
### Gaia REST API
* Gaia
### Gaia CLI
* SDK
### Gaia
* Tendermint
### SDK
### Tendermint
IMPROVEMENTS
<!------------------------------- IMPROVEMENTS ------------------------------->
* Gaia REST API
## IMPROVEMENTS
* Gaia CLI
### Gaia REST API
* Gaia
### Gaia CLI
* SDK
### Gaia
* Tendermint
### SDK
### Tendermint
BUG FIXES
<!--------------------------------- BUG FIXES -------------------------------->
* Gaia REST API
## BUG FIXES
* Gaia CLI
### Gaia REST API
* Gaia
### Gaia CLI
* SDK
### Gaia
* Tendermint
### SDK
### Tendermint

View File

@ -318,16 +318,17 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
// InitChain implements the ABCI interface. It runs the initialization logic
// directly on the CommitMultiStore.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// stash the consensus params in the cms main store and memoize
if req.ConsensusParams != nil {
app.setConsensusParams(req.ConsensusParams)
app.storeConsensusParams(req.ConsensusParams)
}
// initialize the deliver state and check state with ChainID and run initChain
app.setDeliverState(abci.Header{ChainID: req.ChainId})
app.setCheckState(abci.Header{ChainID: req.ChainId})
initHeader := abci.Header{ChainID: req.ChainId, Time: req.Time}
// initialize the deliver state and check state with a correct header
app.setDeliverState(initHeader)
app.setCheckState(initHeader)
if app.initChainer == nil {
return
@ -628,15 +629,9 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Con
return
}
type indexedABCILog struct {
MsgIndex int `json:"msg_index"`
Success bool `json:"success"`
Log string `json:"log"`
}
// runMsgs iterates through all the messages and executes them.
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
idxlogs := make([]indexedABCILog, 0, len(msgs)) // a list of JSON-encoded logs with msg index
idxlogs := make([]sdk.ABCIMessageLog, 0, len(msgs)) // a list of JSON-encoded logs with msg index
var data []byte // NOTE: we just append them all (?!)
var tags sdk.Tags // also just append them all
@ -665,7 +660,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type()))
tags = append(tags, msgResult.Tags...)
idxLog := indexedABCILog{MsgIndex: msgIdx, Log: msgResult.Log}
idxLog := sdk.ABCIMessageLog{MsgIndex: msgIdx, Log: msgResult.Log}
// stop execution and return on first failed message
if !msgResult.IsOK() {

View File

@ -53,6 +53,7 @@ type CLIContext struct {
FromAddress sdk.AccAddress
FromName string
Indent bool
SkipConfirm bool
}
// NewCLIContext returns a new initialized CLIContext with parameters from the
@ -96,6 +97,7 @@ func NewCLIContext() CLIContext {
FromAddress: fromAddress,
FromName: fromName,
Indent: viper.GetBool(client.FlagIndentResponse),
SkipConfirm: viper.GetBool(client.FlagSkipConfirmation),
}
}
@ -254,6 +256,7 @@ func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) {
switch ctx.OutputFormat {
case "text":
out = []byte(toPrint.String())
case "json":
if ctx.Indent {
out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ")

View File

@ -66,15 +66,13 @@ func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) {
return nil, errors.New("account decoder required but not provided")
}
res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore)
res, err := ctx.queryAccount(address)
if err != nil {
return nil, err
} else if len(res) == 0 {
return nil, ErrInvalidAccount(address)
}
account, err := ctx.AccDecoder(res)
if err != nil {
var account auth.Account
if err := ctx.Codec.UnmarshalJSON(res, &account); err != nil {
return nil, err
}
@ -117,32 +115,33 @@ func (ctx CLIContext) GetAccountSequence(address []byte) (uint64, error) {
// error is returned if it does not.
func (ctx CLIContext) EnsureAccountExists() error {
addr := ctx.GetFromAddress()
accountBytes, err := ctx.QueryStore(auth.AddressStoreKey(addr), ctx.AccountStore)
if err != nil {
return err
}
if len(accountBytes) == 0 {
return ErrInvalidAccount(addr)
}
return nil
return ctx.EnsureAccountExistsFromAddr(addr)
}
// EnsureAccountExistsFromAddr ensures that an account exists for a given
// address. Instead of using the context's from name, a direct address is
// given. An error is returned if it does not.
func (ctx CLIContext) EnsureAccountExistsFromAddr(addr sdk.AccAddress) error {
accountBytes, err := ctx.QueryStore(auth.AddressStoreKey(addr), ctx.AccountStore)
_, err := ctx.queryAccount(addr)
return err
}
// queryAccount queries an account using custom query endpoint of auth module
// returns an error if result is `null` otherwise account data
func (ctx CLIContext) queryAccount(addr sdk.AccAddress) ([]byte, error) {
bz, err := ctx.Codec.MarshalJSON(auth.NewQueryAccountParams(addr))
if err != nil {
return err
return nil, err
}
if len(accountBytes) == 0 {
return ErrInvalidAccount(addr)
route := fmt.Sprintf("custom/%s/%s", ctx.AccountStore, auth.QueryAccount)
res, err := ctx.QueryWithData(route, bz)
if err != nil {
return nil, err
}
return nil
return res, nil
}
// query performs a query from a Tendermint node with the provided store name

View File

@ -45,6 +45,7 @@ const (
FlagSSLCertFile = "ssl-certfile"
FlagSSLKeyFile = "ssl-keyfile"
FlagOutputDocument = "output-document" // inspired by wget -O
FlagSkipConfirmation = "yes"
)
// LineBreak can be included in a command list to provide a blank line
@ -89,9 +90,14 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
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")
c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
// --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)", GasFlagAuto, DefaultGasLimit))
"gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)",
GasFlagAuto, DefaultGasLimit,
))
viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode))
viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger))
viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode))

View File

@ -9,7 +9,7 @@ import (
"errors"
"github.com/bgentry/speakeasy"
"github.com/mattn/go-isatty"
isatty "github.com/mattn/go-isatty"
)
// MinPassLength is the minimum acceptable password length
@ -90,8 +90,9 @@ func GetCheckPassword(prompt, prompt2 string, buf *bufio.Reader) (string, error)
func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) {
for {
if inputIsTty() {
fmt.Print(fmt.Sprintf("%s [y/n]:", prompt))
fmt.Print(fmt.Sprintf("%s [Y/n]: ", prompt))
}
response, err := readLineFromBuf(buf)
if err != nil {
return false, err

View File

@ -2,10 +2,7 @@ package keys
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"sort"
@ -13,15 +10,13 @@ import (
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"errors"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/go-bip39"
bip39 "github.com/cosmos/go-bip39"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
@ -147,7 +142,7 @@ func runAddCmd(_ *cobra.Command, args []string) error {
}
pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
if _, err := kb.CreateOffline(name, pk); err != nil {
if _, err := kb.CreateMulti(name, pk); err != nil {
return err
}
@ -247,7 +242,7 @@ func runAddCmd(_ *cobra.Command, args []string) error {
}
}
info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, encryptPassword, account, index)
info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, encryptPassword, account, index)
if err != nil {
return err
}
@ -268,7 +263,7 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
switch output {
case OutputFormatText:
fmt.Fprintln(os.Stderr)
printKeyInfo(info, Bech32KeyOutput)
printKeyInfo(info, keys.Bech32KeyOutput)
// print mnemonic unless requested not to.
if showMnemonic {
@ -278,7 +273,7 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
fmt.Fprintln(os.Stderr, mnemonic)
}
case OutputFormatJSON:
out, err := Bech32KeyOutput(info)
out, err := keys.Bech32KeyOutput(info)
if err != nil {
return err
}
@ -304,191 +299,3 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
return nil
}
/////////////////////////////
// REST
// function to just create a new seed to display in the UI before actually persisting it in the keybase
func generateMnemonic(algo keys.SigningAlgo) string {
kb := keys.NewInMemory()
pass := app.DefaultKeyPass
name := "inmemorykey"
_, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo)
return seed
}
// CheckAndWriteErrorResponse will check for errors and return
// a given error message when corresponding
//TODO: Move to utils/rest or similar
func CheckAndWriteErrorResponse(w http.ResponseWriter, httpErr int, err error) bool {
if err != nil {
w.WriteHeader(httpErr)
_, _ = w.Write([]byte(err.Error()))
return true
}
return false
}
// add new key REST handler
func AddNewKeyRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var kb keys.Keybase
var m AddNewKey
kb, err := NewKeyBaseFromHomeFlag()
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
body, err := ioutil.ReadAll(r.Body)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
err = json.Unmarshal(body, &m)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
// Check parameters
if m.Name == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
return
}
if m.Password == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
return
}
mnemonic := m.Mnemonic
// if mnemonic is empty, generate one
if mnemonic == "" {
mnemonic = generateMnemonic(keys.Secp256k1)
}
if !bip39.IsMnemonicValid(mnemonic) {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
}
if m.Account < 0 || m.Account > maxValidAccountValue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
return
}
if m.Index < 0 || m.Index > maxValidIndexalue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
return
}
_, err = kb.Get(m.Name)
if err == nil {
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(m.Name))
return
}
// create account
account := uint32(m.Account)
index := uint32(m.Index)
info, err := kb.CreateAccount(m.Name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput, err := Bech32KeyOutput(info)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput.Mnemonic = mnemonic
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}
// Seed REST request handler
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
algoType := vars["type"]
// algo type defaults to secp256k1
if algoType == "" {
algoType = "secp256k1"
}
algo := keys.SigningAlgo(algoType)
seed := generateMnemonic(algo)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(seed))
}
// RecoverRequestHandler performs key recover request
func RecoverRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var m RecoverKey
body, err := ioutil.ReadAll(r.Body)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
err = cdc.UnmarshalJSON(body, &m)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
kb, err := NewKeyBaseFromHomeFlag()
CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err)
if name == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
return
}
if m.Password == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
return
}
mnemonic := m.Mnemonic
if !bip39.IsMnemonicValid(mnemonic) {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
}
if m.Mnemonic == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingMnemonic())
return
}
if m.Account < 0 || m.Account > maxValidAccountValue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
return
}
if m.Index < 0 || m.Index > maxValidIndexalue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
return
}
_, err = kb.Get(name)
if err == nil {
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(name))
return
}
account := uint32(m.Account)
index := uint32(m.Index)
info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput, err := Bech32KeyOutput(info)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}

View File

@ -2,13 +2,9 @@ package keys
import (
"bufio"
"net/http"
"strings"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
@ -61,39 +57,3 @@ func Test_runAddCmdBasic(t *testing.T) {
err = runAddCmd(cmd, []string{"keyname2"})
assert.NoError(t, err)
}
type MockResponseWriter struct {
dataHeaderStatus int
dataBody []byte
}
func (MockResponseWriter) Header() http.Header {
panic("Unexpected call!")
}
func (w *MockResponseWriter) Write(data []byte) (int, error) {
w.dataBody = append(w.dataBody, data...)
return len(data), nil
}
func (w *MockResponseWriter) WriteHeader(statusCode int) {
w.dataHeaderStatus = statusCode
}
func TestCheckAndWriteErrorResponse(t *testing.T) {
mockRW := MockResponseWriter{}
mockRW.WriteHeader(100)
assert.Equal(t, 100, mockRW.dataHeaderStatus)
detected := CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, errors.New("some ERROR"))
require.True(t, detected)
require.Equal(t, http.StatusBadRequest, mockRW.dataHeaderStatus)
require.Equal(t, "some ERROR", string(mockRW.dataBody[:]))
mockRW = MockResponseWriter{}
detected = CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, nil)
require.False(t, detected)
require.Equal(t, 0, mockRW.dataHeaderStatus)
require.Equal(t, "", string(mockRW.dataBody[:]))
}

View File

@ -6,35 +6,37 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/crypto/keys"
)
type testCases struct {
Keys []KeyOutput
Answers []KeyOutput
Keys []keys.KeyOutput
Answers []keys.KeyOutput
JSON [][]byte
}
func getTestCases() testCases {
return testCases{
[]KeyOutput{
{"A", "B", "C", "D", "E"},
{"A", "B", "C", "D", ""},
{"", "B", "C", "D", ""},
{"", "", "", "", ""},
[]keys.KeyOutput{
{"A", "B", "C", "D", "E", 0, nil},
{"A", "B", "C", "D", "", 0, nil},
{"", "B", "C", "D", "", 0, nil},
{"", "", "", "", "", 0, nil},
},
make([]KeyOutput, 4),
make([]keys.KeyOutput, 4),
[][]byte{
[]byte(`{"name":"A","type":"B","address":"C","pub_key":"D","mnemonic":"E"}`),
[]byte(`{"name":"A","type":"B","address":"C","pub_key":"D"}`),
[]byte(`{"name":"","type":"B","address":"C","pub_key":"D"}`),
[]byte(`{"name":"","type":"","address":"","pub_key":""}`),
[]byte(`{"name":"A","type":"B","address":"C","pubkey":"D","mnemonic":"E"}`),
[]byte(`{"name":"A","type":"B","address":"C","pubkey":"D"}`),
[]byte(`{"name":"","type":"B","address":"C","pubkey":"D"}`),
[]byte(`{"name":"","type":"","address":"","pubkey":""}`),
},
}
}
func TestMarshalJSON(t *testing.T) {
type args struct {
o KeyOutput
o keys.KeyOutput
}
data := getTestCases()

View File

@ -2,19 +2,14 @@ package keys
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"github.com/spf13/viper"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
"github.com/spf13/cobra"
)
@ -101,51 +96,3 @@ func confirmDeletion(buf *bufio.Reader) error {
}
return nil
}
////////////////////////
// REST
// delete key request REST body
type DeleteKeyBody struct {
Password string `json:"password"`
}
// delete key REST handler
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var kb keys.Keybase
var m DeleteKeyBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
kb, err = NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
err = kb.Delete(name, m.Password, false)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if keyerror.IsErrWrongPassword(err) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusOK)
}

View File

@ -1,9 +1,6 @@
package keys
import (
"net/http"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/spf13/cobra"
)
@ -29,36 +26,3 @@ func runListCmd(cmd *cobra.Command, args []string) error {
}
return err
}
/////////////////////////
// REST
// query key list REST handler
func QueryKeysRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
kb, err := NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
infos, err := kb.List()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
// an empty list will be JSONized as null, but we want to keep the empty list
if len(infos) == 0 {
rest.PostProcessResponse(w, cdc, []string{}, indent)
return
}
keysOutput, err := Bech32KeysOutput(infos)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
rest.PostProcessResponse(w, cdc, keysOutput, indent)
}
}

View File

@ -1,7 +1,6 @@
package keys
import (
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
@ -30,14 +29,3 @@ func Commands() *cobra.Command {
)
return cmd
}
// resgister REST routes
func RegisterRoutes(r *mux.Router, indent bool) {
r.HandleFunc("/keys", QueryKeysRequestHandler(indent)).Methods("GET")
r.HandleFunc("/keys", AddNewKeyRequestHandler(indent)).Methods("POST")
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
r.HandleFunc("/keys/{name}/recover", RecoverRequestHandler(indent)).Methods("POST")
r.HandleFunc("/keys/{name}", GetKeyRequestHandler(indent)).Methods("GET")
r.HandleFunc("/keys/{name}", UpdateKeyRequestHandler).Methods("PUT")
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
}

View File

@ -4,8 +4,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/gorilla/mux"
)
func TestCommands(t *testing.T) {
@ -15,8 +13,3 @@ func TestCommands(t *testing.T) {
// Commands are registered
assert.Equal(t, 7, len(rootCommands.Commands()))
}
func TestRegisterRoutes(t *testing.T) {
fakeRouter := mux.Router{}
RegisterRoutes(&fakeRouter, false)
}

View File

@ -1,24 +1,19 @@
package keys
import (
"fmt"
"net/http"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/types/rest"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
@ -28,36 +23,32 @@ const (
FlagPublicKey = "pubkey"
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
FlagBechPrefix = "bech"
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
FlagDevice = "device"
flagMultiSigThreshold = "multisig-threshold"
flagShowMultiSig = "show-multisig"
flagMultiSigThreshold = "multisig-threshold"
defaultMultiSigKeyName = "multi"
)
var _ keys.Info = (*multiSigKey)(nil)
type multiSigKey struct {
name string
key crypto.PubKey
}
func (m multiSigKey) GetName() string { return m.name }
func (m multiSigKey) GetType() keys.KeyType { return keys.TypeLocal }
func (m multiSigKey) GetPubKey() crypto.PubKey { return m.key }
func (m multiSigKey) GetAddress() sdk.AccAddress { return sdk.AccAddress(m.key.Address()) }
func showKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show [name]",
Use: "show [name [name...]]",
Short: "Show key info for the given name",
Long: `Return public details of one local key.`,
Args: cobra.MinimumNArgs(1),
RunE: runShowCmd,
Long: `Return public details of a single local key. If multiple names are
provided, then an ephemeral multisig key will be created under the name "multi"
consisting of all the keys provided by name and multisig threshold.`,
Args: cobra.MinimumNArgs(1),
RunE: runShowCmd,
}
cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)")
cmd.Flags().BoolP(FlagAddress, "a", false, "output the address only (overrides --output)")
cmd.Flags().BoolP(FlagPublicKey, "p", false, "output the public key only (overrides --output)")
cmd.Flags().String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)")
cmd.Flags().BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)")
cmd.Flags().BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)")
cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in the device")
cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures")
cmd.Flags().BoolP(flagShowMultiSig, "m", false, "Output multisig pubkey constituents, threshold, and weights")
return cmd
}
@ -71,12 +62,13 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
return err
}
} else {
pks := make([]crypto.PubKey, len(args))
pks := make([]tmcrypto.PubKey, len(args))
for i, keyName := range args {
info, err := GetKeyInfo(keyName)
if err != nil {
return err
}
pks[i] = info.GetPubKey()
}
@ -85,15 +77,15 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
if err != nil {
return err
}
multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
info = multiSigKey{
name: defaultMultiSigKeyName,
key: multikey,
}
info = keys.NewMultiInfo(defaultMultiSigKeyName, multikey)
}
isShowAddr := viper.GetBool(FlagAddress)
isShowPubKey := viper.GetBool(FlagPublicKey)
isShowDevice := viper.GetBool(FlagDevice)
isShowMultiSig := viper.GetBool(flagShowMultiSig)
isOutputSet := false
tmp := cmd.Flag(cli.OutputFlag)
@ -119,10 +111,32 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
printKeyAddress(info, bechKeyOut)
case isShowPubKey:
printPubKey(info, bechKeyOut)
case isShowMultiSig:
printMultiSigKeyInfo(info, bechKeyOut)
default:
printKeyInfo(info, bechKeyOut)
}
if isShowDevice {
if isShowPubKey {
return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys")
}
if viper.GetString(FlagBechPrefix) != "acc" {
return fmt.Errorf("the device flag (-d) can only be used for accounts")
}
// Override and show in the device
if info.GetType() != keys.TypeLedger {
return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices")
}
hdpath, err := info.GetPath()
if err != nil {
return nil
}
return crypto.LedgerShowAddress(*hdpath, info.GetPubKey())
}
return nil
}
@ -139,56 +153,13 @@ func validateMultisigThreshold(k, nKeys int) error {
func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
switch bechPrefix {
case "acc":
return Bech32KeyOutput, nil
case "val":
return Bech32ValKeyOutput, nil
case "cons":
return Bech32ConsKeyOutput, nil
case sdk.PrefixAccount:
return keys.Bech32KeyOutput, nil
case sdk.PrefixValidator:
return keys.Bech32ValKeyOutput, nil
case sdk.PrefixConsensus:
return keys.Bech32ConsKeyOutput, nil
}
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
}
///////////////////////////
// REST
// get key REST handler
func GetKeyRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
if bechPrefix == "" {
bechPrefix = "acc"
}
bechKeyOut, err := getBechKeyOut(bechPrefix)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
info, err := GetKeyInfo(name)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
keyOutput, err := bechKeyOut(info)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}

View File

@ -3,29 +3,28 @@ package keys
import (
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/cli"
)
func Test_multiSigKey_Properties(t *testing.T) {
tmpKey1 := secp256k1.GenPrivKeySecp256k1([]byte("mySecret"))
tmp := multiSigKey{
name: "myMultisig",
key: tmpKey1.PubKey(),
}
pk := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey1.PubKey()})
tmp := keys.NewMultiInfo("myMultisig", pk)
assert.Equal(t, "myMultisig", tmp.GetName())
assert.Equal(t, keys.TypeLocal, tmp.GetType())
assert.Equal(t, "015ABFFB09DB738A45745A91E8C401423ECE4016", tmp.GetPubKey().Address().String())
assert.Equal(t, "cosmos1q9dtl7cfmdec53t5t2g733qpgglvusqk6xdntl", tmp.GetAddress().String())
assert.Equal(t, keys.TypeMulti, tmp.GetType())
assert.Equal(t, "79BF2B5B418A85329EC2149D1854D443F56F5A9F", tmp.GetPubKey().Address().String())
assert.Equal(t, "cosmos10xljkk6p32zn98kzzjw3s4x5g06k7k5lz6flcv", tmp.GetAddress().String())
}
func Test_showKeysCmd(t *testing.T) {
@ -64,21 +63,36 @@ func Test_runShowCmd(t *testing.T) {
assert.EqualError(t, err, "invalid Bech32 prefix encoding provided: ")
// Now try single key - set bech to acc
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
err = runShowCmd(cmd, []string{fakeKeyName1})
assert.NoError(t, err)
// Now try multisig key - set bech to acc
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "threshold must be a positive integer")
// Now try multisig key - set bech to acc + threshold=2
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagBechPrefix, sdk.PrefixAccount)
viper.Set(flagMultiSigThreshold, 2)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.NoError(t, err)
// Now try multisig key - set bech to acc + threshold=2
viper.Set(FlagBechPrefix, "acc")
viper.Set(FlagDevice, true)
viper.Set(flagMultiSigThreshold, 2)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices")
viper.Set(FlagBechPrefix, "val")
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for accounts")
viper.Set(FlagPublicKey, true)
err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2})
assert.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys")
// TODO: Capture stdout and compare
}
@ -119,9 +133,9 @@ func Test_getBechKeyOut(t *testing.T) {
}{
{"empty", args{""}, nil, true},
{"wrong", args{"???"}, nil, true},
{"acc", args{"acc"}, Bech32KeyOutput, false},
{"val", args{"val"}, Bech32ValKeyOutput, false},
{"cons", args{"cons"}, Bech32ConsKeyOutput, false},
{"acc", args{sdk.PrefixAccount}, keys.Bech32KeyOutput, false},
{"val", args{sdk.PrefixValidator}, keys.Bech32ValKeyOutput, false},
{"cons", args{sdk.PrefixConsensus}, keys.Bech32ConsKeyOutput, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -1,13 +1,6 @@
package keys
// used for outputting keys.Info over REST
type KeyOutput struct {
Name string `json:"name"`
Type string `json:"type"`
Address string `json:"address"`
PubKey string `json:"pub_key"`
Mnemonic string `json:"mnemonic,omitempty"`
}
// AddNewKey request a new key
type AddNewKey struct {

View File

@ -1,18 +1,11 @@
package keys
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
)
func updateKeyCommand() *cobra.Command {
@ -52,55 +45,3 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
fmt.Println("Password successfully updated!")
return nil
}
///////////////////////
// REST
// update key request REST body
type UpdateKeyBody struct {
NewPassword string `json:"new_password"`
OldPassword string `json:"old_password"`
}
// update key REST handler
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var kb keys.Keybase
var m UpdateKeyBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
kb, err = NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
getNewpass := func() (string, error) { return m.NewPassword, nil }
err = kb.Update(name, m.OldPassword, getNewpass)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if keyerror.IsErrWrongPassword(err) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

View File

@ -9,7 +9,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// available output formats.
@ -21,7 +20,7 @@ const (
defaultKeyDBName = "keys"
)
type bechKeyOutFn func(keyInfo keys.Info) (KeyOutput, error)
type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error)
// GetKeyInfo returns key info for a given name. An error is returned if the
// keybase cannot be retrieved or getting the info fails.
@ -90,68 +89,22 @@ func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil
}
// create a list of KeyOutput in bech32 format
func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
kos := make([]KeyOutput, len(infos))
for i, info := range infos {
ko, err := Bech32KeyOutput(info)
if err != nil {
return nil, err
}
kos[i] = ko
}
return kos, nil
func printKeyTextHeader() {
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
}
// create a KeyOutput in bech32 format
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
accAddr := sdk.AccAddress(info.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: info.GetName(),
Type: info.GetType().String(),
Address: accAddr.String(),
PubKey: bechPubKey,
}, nil
func printMultiSigKeyTextHeader() {
fmt.Printf("WEIGHT:\tTHRESHOLD:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
}
// Bech32ConsKeyOutput returns key output for a consensus node's key
// information.
func Bech32ConsKeyOutput(keyInfo keys.Info) (KeyOutput, error) {
consAddr := sdk.ConsAddress(keyInfo.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyConsPub(keyInfo.GetPubKey())
func printMultiSigKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(keyInfo)
if err != nil {
return KeyOutput{}, err
panic(err)
}
return KeyOutput{
Name: keyInfo.GetName(),
Type: keyInfo.GetType().String(),
Address: consAddr.String(),
PubKey: bechPubKey,
}, nil
}
// Bech32ValKeyOutput returns key output for a validator's key information.
func Bech32ValKeyOutput(keyInfo keys.Info) (KeyOutput, error) {
valAddr := sdk.ValAddress(keyInfo.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyValPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: keyInfo.GetName(),
Type: keyInfo.GetType().String(),
Address: valAddr.String(),
PubKey: bechPubKey,
}, nil
printMultiSigKeyTextHeader()
printMultiSigKeyOutput(ko)
}
func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
@ -162,8 +115,9 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
switch viper.Get(cli.OutputFlag) {
case OutputFormatText:
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
printKeyTextHeader()
printKeyOutput(ko)
case "json":
out, err := MarshalJSON(ko)
if err != nil {
@ -175,29 +129,38 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
}
func printInfos(infos []keys.Info) {
kos, err := Bech32KeysOutput(infos)
kos, err := keys.Bech32KeysOutput(infos)
if err != nil {
panic(err)
}
switch viper.Get(cli.OutputFlag) {
case OutputFormatText:
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
printKeyTextHeader()
for _, ko := range kos {
printKeyOutput(ko)
}
case OutputFormatJSON:
out, err := MarshalJSON(kos)
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
}
func printKeyOutput(ko KeyOutput) {
func printKeyOutput(ko keys.KeyOutput) {
fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey)
}
func printMultiSigKeyOutput(ko keys.KeyOutput) {
for _, pk := range ko.PubKeys {
fmt.Printf("%d\t%d\t\t%s\t%s\n", pk.Weight, ko.Threshold, pk.Address, pk.PubKey)
}
}
func printKeyAddress(info keys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {

View File

@ -11,11 +11,11 @@ import (
"testing"
"time"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/tests"
@ -23,7 +23,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/rest"
"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/bank"
dclcommon "github.com/cosmos/cosmos-sdk/x/distribution/client/common"
distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest"
@ -48,119 +47,6 @@ func init() {
version.Version = os.Getenv("VERSION")
}
func TestSeedsAreDifferent(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr, _ := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
mnemonic1 := getKeysSeed(t, port)
mnemonic2 := getKeysSeed(t, port)
require.NotEqual(t, mnemonic1, mnemonic2)
}
func TestKeyRecover(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true)
defer cleanup()
myName1 := "TestKeyRecover_1"
myName2 := "TestKeyRecover_2"
mnemonic := getKeysSeed(t, port)
expectedInfo, _ := kb.CreateAccount(myName1, mnemonic, "", pw, 0, 0)
expectedAddress := expectedInfo.GetAddress().String()
expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey())
// recover key
doRecoverKey(t, port, myName2, pw, mnemonic, 0, 0)
keys := getKeys(t, port)
require.Equal(t, expectedAddress, keys[0].Address)
require.Equal(t, expectedPubKey, keys[0].PubKey)
}
func TestKeyRecoverHDPath(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true)
defer cleanup()
mnemonic := getKeysSeed(t, port)
for account := uint32(0); account < 50; account += 13 {
for index := uint32(0); index < 50; index += 15 {
name1Idx := fmt.Sprintf("name1_%d_%d", account, index)
name2Idx := fmt.Sprintf("name2_%d_%d", account, index)
expectedInfo, _ := kb.CreateAccount(name1Idx, mnemonic, "", pw, account, index)
expectedAddress := expectedInfo.GetAddress().String()
expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey())
// recover key
doRecoverKey(t, port, name2Idx, pw, mnemonic, account, index)
keysName2Idx := getKey(t, port, name2Idx)
require.Equal(t, expectedAddress, keysName2Idx.Address)
require.Equal(t, expectedPubKey, keysName2Idx.PubKey)
}
}
}
func TestKeys(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr1, _ := CreateAddr(t, name1, pw, kb)
addr1Bech32 := addr1.String()
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr1}, true)
defer cleanup()
// get new seed & recover key
mnemonic2 := getKeysSeed(t, port)
doRecoverKey(t, port, name2, pw, mnemonic2, 0, 0)
// add key
mnemonic3 := mnemonic2
resp := doKeysPost(t, port, name3, pw, mnemonic3, 0, 0)
addr3Bech32 := resp.Address
_, err = sdk.AccAddressFromBech32(addr3Bech32)
require.NoError(t, err, "Failed to return a correct bech32 address")
// test if created account is the correct account
expectedInfo3, _ := kb.CreateAccount(name3, mnemonic3, "", pw, 0, 0)
expectedAddress3 := sdk.AccAddress(expectedInfo3.GetPubKey().Address()).String()
require.Equal(t, expectedAddress3, addr3Bech32)
// existing keys
require.Equal(t, name1, getKey(t, port, name1).Name, "Did not serve keys name correctly")
require.Equal(t, addr1Bech32, getKey(t, port, name1).Address, "Did not serve keys Address correctly")
require.Equal(t, name2, getKey(t, port, name2).Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, getKey(t, port, name2).Address, "Did not serve keys Address correctly")
require.Equal(t, name3, getKey(t, port, name3).Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, getKey(t, port, name3).Address, "Did not serve keys Address correctly")
// select key
key := getKey(t, port, name3)
require.Equal(t, name3, key.Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, key.Address, "Did not serve keys Address correctly")
// update key
updateKey(t, port, name3, pw, altPw, false)
// here it should say unauthorized as we changed the password before
updateKey(t, port, name3, pw, altPw, true)
// delete key
deleteKey(t, port, name3, altPw)
}
func TestVersion(t *testing.T) {
// skip the test if the VERSION environment variable has not been set
if version.Version == "" {
@ -242,7 +128,7 @@ func TestCoinSend(t *testing.T) {
// query sender
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, sdk.DefaultBondDenom, coins[0].Denom)
require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount)
@ -255,7 +141,7 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, int64(1), coins2[0].Amount.Int64())
// test failure with too little gas
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "100", 0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "100", 0, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
require.Nil(t, err)
@ -268,11 +154,11 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, http.StatusBadRequest, res.StatusCode, body)
// test failure with 0 gas
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "0", 0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "0", 0, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
// test failure with wrong adjustment
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, client.GasFlagAuto, 0.1, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, client.GasFlagAuto, 0.1, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
@ -291,7 +177,7 @@ func TestCoinSend(t *testing.T) {
// run successful tx
gas := fmt.Sprintf("%d", gasEstResp.GasEstimate)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, gas, 1.0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, gas, 1.0, false, true, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
@ -301,7 +187,7 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, uint32(0), resultTx.Code)
acc = getAccount(t, port, addr)
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.Equal(t, expectedBalance.Amount.SubRaw(1), acc.GetCoins().AmountOf(sdk.DefaultBondDenom))
}
@ -316,13 +202,15 @@ func TestCoinSendAccAuto(t *testing.T) {
initialBalance := acc.GetCoins()
// send a transfer tx without specifying account number and sequence
res, body, _ := doTransferWithGasAccAuto(t, port, seed, name1, memo, pw, "200000", 1.0, false, false, fees)
res, body, _ := doTransferWithGasAccAuto(
t, port, seed, name1, memo, pw, addr, "200000", 1.0, false, true, fees,
)
require.Equal(t, http.StatusOK, res.StatusCode, body)
// query sender
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, sdk.DefaultBondDenom, coins[0].Denom)
require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount)
@ -336,7 +224,7 @@ func TestCoinMultiSendGenerateOnly(t *testing.T) {
defer cleanup()
// generate only
res, body, _ := doTransferWithGas(t, port, seed, "", memo, "", addr, "200000", 1, false, true, fees)
res, body, _ := doTransferWithGas(t, port, seed, "", memo, "", addr, "200000", 1, false, false, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var stdTx auth.StdTx
@ -356,6 +244,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
require.NoError(t, err)
addr, seed := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
acc := getAccount(t, port, addr)
@ -371,95 +260,57 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
// generate tx
gas := fmt.Sprintf("%d", gasEstResp.GasEstimate)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, "", addr, gas, 1, false, true, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, "", addr, gas, 1, false, false, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var msg auth.StdTx
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg))
require.Equal(t, len(msg.Msgs), 1)
require.Equal(t, msg.Msgs[0].Route(), "bank")
require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr})
require.Equal(t, 0, len(msg.Signatures))
require.Equal(t, memo, msg.Memo)
require.NotZero(t, msg.Fee.Gas)
var tx auth.StdTx
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &tx))
require.Equal(t, len(tx.Msgs), 1)
require.Equal(t, tx.Msgs[0].Route(), "bank")
require.Equal(t, tx.Msgs[0].GetSigners(), []sdk.AccAddress{addr})
require.Equal(t, 0, len(tx.Signatures))
require.Equal(t, memo, tx.Memo)
require.NotZero(t, tx.Fee.Gas)
gasEstimate := int64(msg.Fee.Gas)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
// sign tx
var signedMsg auth.StdTx
payload := authrest.SignBody{
Tx: msg,
BaseReq: rest.NewBaseReq(
name1, pw, "", viper.GetString(client.FlagChainID), "", "",
accnum, sequence, nil, nil, false, false,
),
}
json, err := cdc.MarshalJSON(payload)
require.Nil(t, err)
res, body = Request(t, port, "POST", "/tx/sign", json)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg))
require.Equal(t, len(msg.Msgs), len(signedMsg.Msgs))
require.Equal(t, msg.Msgs[0].Type(), signedMsg.Msgs[0].Type())
require.Equal(t, msg.Msgs[0].GetSigners(), signedMsg.Msgs[0].GetSigners())
require.Equal(t, 1, len(signedMsg.Signatures))
// broadcast tx
broadcastPayload := struct {
Tx auth.StdTx `json:"tx"`
Return string `json:"return"`
}{Tx: signedMsg, Return: "block"}
json, err = cdc.MarshalJSON(broadcastPayload)
require.Nil(t, err)
res, body = Request(t, port, "POST", "/tx/broadcast", json)
require.Equal(t, http.StatusOK, res.StatusCode, body)
gasEstimate := int64(tx.Fee.Gas)
_, body = signAndBroadcastGenTx(t, port, name1, pw, body, acc, 1.0, false)
// check if tx was committed
var resultTx sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
require.Equal(t, uint32(0), resultTx.Code)
require.Equal(t, gasEstimate, resultTx.GasWanted)
var txResp sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &txResp))
require.Equal(t, uint32(0), txResp.Code)
require.Equal(t, gasEstimate, txResp.GasWanted)
}
func TestEncodeTx(t *testing.T) {
// Setup
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr, seed := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
// Make a transaction to test with
res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, "2", 1, false, true, fees)
res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, "2", 1, false, false, fees)
var tx auth.StdTx
cdc.UnmarshalJSON([]byte(body), &tx)
// Build the request
encodeReq := struct {
Tx auth.StdTx `json:"tx"`
}{Tx: tx}
encodedJSON, _ := cdc.MarshalJSON(encodeReq)
res, body = Request(t, port, "POST", "/tx/encode", encodedJSON)
req := clienttx.EncodeReq{Tx: tx}
encodedJSON, _ := cdc.MarshalJSON(req)
res, body = Request(t, port, "POST", "/txs/encode", encodedJSON)
// Make sure it came back ok, and that we can decode it back to the transaction
// 200 response
// 200 response.
require.Equal(t, http.StatusOK, res.StatusCode, body)
encodeResp := struct {
Tx string `json:"tx"`
}{}
// No error decoding the JSON
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &encodeResp))
// Check that the base64 decodes
// verify that the base64 decodes
decodedBytes, err := base64.StdEncoding.DecodeString(encodeResp.Tx)
require.Nil(t, err)
// Check that the transaction decodes as expected
// check that the transaction decodes as expected
var decodedTx auth.StdTx
require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx))
require.Equal(t, memo, decodedTx.Memo)
@ -562,7 +413,7 @@ func TestValidatorsQuery(t *testing.T) {
foundVal = true
}
require.True(t, foundVal, "pk %v, operator %v", operAddrs[0], validators[0].OperatorAddr)
require.True(t, foundVal, "pk %v, operator %v", operAddrs[0], validators[0].OperatorAddress)
}
func TestValidatorQuery(t *testing.T) {
@ -572,7 +423,7 @@ func TestValidatorQuery(t *testing.T) {
require.Equal(t, 1, len(operAddrs))
validator := getValidator(t, port, operAddrs[0])
require.Equal(t, validator.OperatorAddr, operAddrs[0], "The returned validator does not hold the correct data")
require.Equal(t, validator.OperatorAddress, operAddrs[0], "The returned validator does not hold the correct data")
}
func TestBonding(t *testing.T) {
@ -588,7 +439,7 @@ func TestBonding(t *testing.T) {
require.Equal(t, 2, len(operAddrs))
amt := sdk.TokensFromTendermintPower(60)
amtDec := sdk.NewDecFromInt(amt)
amtDec := amt.ToDec()
validator := getValidator(t, port, operAddrs[0])
acc := getAccount(t, port, addr)
@ -612,7 +463,7 @@ func TestBonding(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(delTokens), coins.AmountOf(sdk.DefaultBondDenom))
expectedBalance = coins[0]
@ -630,11 +481,11 @@ func TestBonding(t *testing.T) {
bondedValidators := getDelegatorValidators(t, port, addr)
require.Len(t, bondedValidators, 1)
require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr)
require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddress)
require.Equal(t, validator.DelegatorShares.Add(amtDec).String(), bondedValidators[0].DelegatorShares.String())
bondedValidator := getDelegatorValidator(t, port, addr, operAddrs[0])
require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr)
require.Equal(t, operAddrs[0], bondedValidator.OperatorAddress)
// testing unbonding
unbondingTokens := sdk.TokensFromTendermintPower(30)
@ -646,7 +497,7 @@ func TestBonding(t *testing.T) {
// sender should have not received any coins as the unbonding has only just begun
acc = getAccount(t, port, addr)
coins = acc.GetCoins()
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.True(t,
expectedBalance.Amount.LT(coins.AmountOf(sdk.DefaultBondDenom)) ||
expectedBalance.Amount.Equal(coins.AmountOf(sdk.DefaultBondDenom)),
@ -664,7 +515,7 @@ func TestBonding(t *testing.T) {
ubd := getUnbondingDelegation(t, port, addr, operAddrs[0])
require.Len(t, ubd.Entries, 1)
require.Equal(t, delTokens.DivRaw(2), ubd.Entries[0].Balance)
require.Equal(t, delTokens.QuoRaw(2), ubd.Entries[0].Balance)
// test redelegation
rdTokens := sdk.TokensFromTendermintPower(30)
@ -675,7 +526,7 @@ func TestBonding(t *testing.T) {
// verify balance after paying fees
acc = getAccount(t, port, addr)
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.True(t,
expectedBalance.Amount.LT(coins.AmountOf(sdk.DefaultBondDenom)) ||
expectedBalance.Amount.Equal(coins.AmountOf(sdk.DefaultBondDenom)),
@ -692,10 +543,18 @@ func TestBonding(t *testing.T) {
require.Equal(t, resultTx.Height, txs[0].Height)
// query delegations, unbondings and redelegations from validator and delegator
rdShares := sdk.NewDecFromInt(rdTokens)
delegatorDels = getDelegatorDelegations(t, port, addr)
require.Len(t, delegatorDels, 1)
require.Equal(t, rdShares, delegatorDels[0].GetShares())
require.Equal(t, operAddrs[1], delegatorDels[0].ValidatorAddress)
// because the second validator never signs during these tests, if this
// this test takes a long time to run, eventually this second validator
// will get slashed, meaning that it's exchange rate is no-longer 1-to-1,
// hence we utilize the exchange rate in the following test
validator2 := getValidator(t, port, operAddrs[1])
delTokensAfterRedelegation := validator2.ShareTokens(delegatorDels[0].GetShares())
require.Equal(t, rdTokens.ToDec(), delTokensAfterRedelegation)
redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1])
require.Len(t, redelegation, 1)
@ -759,7 +618,7 @@ func TestSubmitProposal(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), acc.GetCoins().AmountOf(sdk.DefaultBondDenom))
// query proposal
@ -795,7 +654,7 @@ func TestDeposit(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), coins.AmountOf(sdk.DefaultBondDenom))
expectedBalance = coins[0]
@ -810,7 +669,7 @@ func TestDeposit(t *testing.T) {
// verify balance after deposit and fee
acc = getAccount(t, port, addr)
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(depositTokens), acc.GetCoins().AmountOf(sdk.DefaultBondDenom))
// query tx
@ -852,7 +711,7 @@ func TestVote(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
expectedBalance := initialBalance[0].Minus(fees[0])
expectedBalance := initialBalance[0].Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), coins.AmountOf(sdk.DefaultBondDenom))
expectedBalance = coins[0]
@ -868,7 +727,7 @@ func TestVote(t *testing.T) {
// verify balance after vote and fee
acc = getAccount(t, port, addr)
coins = acc.GetCoins()
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.Equal(t, expectedBalance.Amount, coins.AmountOf(sdk.DefaultBondDenom))
expectedBalance = coins[0]
@ -892,7 +751,7 @@ func TestVote(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
coins = acc.GetCoins()
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.Equal(t, expectedBalance.Amount.Sub(delTokens), coins.AmountOf(sdk.DefaultBondDenom))
expectedBalance = coins[0]
@ -905,7 +764,7 @@ func TestVote(t *testing.T) {
// verify balance
acc = getAccount(t, port, addr)
expectedBalance = expectedBalance.Minus(fees[0])
expectedBalance = expectedBalance.Sub(fees[0])
require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(sdk.DefaultBondDenom))
tally = getTally(t, port, proposalID)
@ -939,7 +798,7 @@ func TestProposalsQuery(t *testing.T) {
defer cleanup()
depositParam := getDepositParam(t, port)
halfMinDeposit := depositParam.MinDeposit.AmountOf(sdk.DefaultBondDenom).DivRaw(2)
halfMinDeposit := depositParam.MinDeposit.AmountOf(sdk.DefaultBondDenom).QuoRaw(2)
getVotingParam(t, port)
getTallyingParam(t, port)
@ -1086,7 +945,7 @@ func TestDistributionFlow(t *testing.T) {
operAddr := sdk.AccAddress(valAddr)
var rewards sdk.DecCoins
res, body := Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil)
res, body := Request(t, port, "GET", fmt.Sprintf("/distribution/validators/%s/outstanding_rewards", valAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards))
@ -1108,7 +967,7 @@ func TestDistributionFlow(t *testing.T) {
require.Equal(t, uint32(0), resultTx.Code)
// Query outstanding rewards changed
res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil)
res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/validators/%s/outstanding_rewards", valAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards))

View File

@ -3,14 +3,12 @@ swagger: "2.0"
info:
version: "3.0"
title: Gaia-Lite for Cosmos
description: A REST interface for state queries, transaction generation, signing, and broadcast.
description: A REST interface for state queries, transaction generation and broadcasting.
tags:
- name: ICS0
description: Tendermint APIs, such as query blocks, transactions and validatorset
- name: ICS1
description: Key management APIs
- name: ICS20
description: Create, sign and broadcast transactions
description: Create and broadcast transactions
- name: ICS21
description: Stake module APIs
- name: ICS22
@ -243,8 +241,8 @@ paths:
post:
tags:
- ICS0
summary: broadcast Tx
description: broadcast tx with tendermint rpc
summary: Broadcast a signed tx
description: Broadcast a signed tx to a full node
consumes:
- application/json
produces:
@ -252,93 +250,28 @@ paths:
parameters:
- in: body
name: txBroadcast
description: Build a StdTx transaction and serilize it to a byte array with amino, then the `"tx"` field in the post body will be the base64 encoding of the byte array. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away).
description: The tx must be a signed StdTx. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away).
required: true
schema:
type: object
properties:
tx:
type: string
$ref: "#/definitions/StdTx"
return:
type: string
example: block
responses:
200:
description: Broadcast tx result
description: Tx broadcasting result
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
500:
description: Internal Server Error
/tx/sign:
/txs/encode:
post:
tags:
- ICS20
summary: Sign a Tx
description: Sign a Tx providing locally stored account and according password
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: sendToken
description: sign tx
required: true
schema:
type: object
properties:
base_req:
$ref: "#/definitions/BaseReq"
tx:
$ref: "#/definitions/StdTx"
append_sig:
type: boolean
example: true
responses:
200:
description: The signed Tx
schema:
$ref: "#/definitions/StdTx"
400:
description: The Tx was malformated or key doesn't exist
401:
description: Key password is wrong
500:
description: Server internal error
/tx/broadcast:
post:
tags:
- ICS20
summary: Send a signed Tx
description: Send a signed Tx to a Gaiad full node
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: txBroadcast
description: broadcast tx
required: true
schema:
type: object
properties:
tx:
$ref: "#/definitions/StdTx"
responses:
202:
description: Tx was send and will probably be added to the next block
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: The Tx was malformated
500:
description: Server internal error
/tx/encode:
post:
tags:
- ICS20
summary: Encode a transaction to wire format
- ICS0
summary: Encode a transaction to the Amino wire format
description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes
consumes:
- application/json
@ -347,7 +280,7 @@ paths:
parameters:
- in: body
name: tx
description: The transaction to encode
description: The tx to encode
required: true
schema:
type: object
@ -356,15 +289,15 @@ paths:
$ref: "#/definitions/StdTx"
responses:
200:
description: Transaction was successfully decoded and re-encoded
description: The tx was successfully decoded and re-encoded
schema:
type: object
properties:
tx:
type: string
example: The base64-encoded Amino-serialized bytes for the transaction
example: The base64-encoded Amino-serialized bytes for the tx
400:
description: The Tx was malformated
description: The tx was malformated
500:
description: Server internal error
/bank/balances/{address}:
@ -393,8 +326,7 @@ paths:
description: Server internal error
/bank/accounts/{address}/transfers:
post:
summary: Send coins (build -> sign -> send)
description: Send coins (build -> sign -> send)
summary: Send coins from one account to another
tags:
- ICS20
consumes:
@ -409,7 +341,7 @@ paths:
type: string
- in: body
name: account
description: The password of the account to remove from the KMS
description: The sender and tx information
required: true
schema:
type: object
@ -422,188 +354,13 @@ paths:
$ref: "#/definitions/Coin"
responses:
202:
description: Tx was send and will probably be added to the next block
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid request
401:
description: Key password is wrong
500:
description: Server internal error
/keys:
get:
summary: List of accounts stored locally
tags:
- ICS1
produces:
- application/json
responses:
200:
description: Array of accounts
schema:
type: array
items:
$ref: "#/definitions/KeyOutput"
500:
description: Server internal error
post:
summary: Create a new account locally
tags:
- ICS1
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: account
description: The account to create
schema:
type: object
required:
- name
- password
- seed
properties:
name:
type: string
password:
type: string
seed:
type: string
responses:
200:
description: Returns account information of the created key
schema:
$ref: "#/definitions/KeyOutput"
400:
description: Invalid request
409:
description: Key name confliction
500:
description: Server internal error
/keys/seed:
get:
summary: Create a new seed to create a new account with
tags:
- ICS1
responses:
200:
description: 24 word Seed
schema:
type: string
example: blossom pool issue kidney elevator blame furnace winter account merry vessel security depend exact travel bargain problem jelly rural net again mask roast chest
/keys/{name}/recover:
post:
summary: Recover a account from a seed
tags:
- ICS1
consumes:
- application/json
produces:
- application/json
parameters:
- in: path
name: name
description: Account name
required: true
type: string
- in: body
name: pwdAndSeed
description: Provide password and seed to recover a key
schema:
type: object
required:
- password
- seed
properties:
password:
type: string
seed:
type: string
responses:
200:
description: Returns account information of the recovered key
schema:
$ref: "#/definitions/KeyOutput"
400:
description: Invalid request
409:
description: Key name confliction
500:
description: Server internal error
/keys/{name}:
parameters:
- in: path
name: name
description: Account name
required: true
type: string
get:
summary: Get a certain locally stored account
tags:
- ICS1
produces:
- application/json
responses:
200:
description: Locally stored account
schema:
$ref: "#/definitions/KeyOutput"
404:
description: Key doesn't exist
put:
summary: Update the password for this account in the KMS
tags:
- ICS1
consumes:
- application/json
parameters:
- in: body
name: account
description: The new and old password
schema:
type: object
required:
- new_password
- old_password
properties:
new_password:
type: string
old_password:
type: string
responses:
200:
description: Updated password
401:
description: Key password is wrong
404:
description: Key doesn't exist
delete:
summary: Remove an account
tags:
- ICS1
consumes:
- application/json
parameters:
- in: body
name: account
description: The password of the account to remove from the KMS
schema:
type: object
required:
- password
properties:
password:
type: string
responses:
200:
description: Removed account
401:
description: Key password is wrong
404:
description: Key doesn't exist
/auth/accounts/{address}:
get:
summary: Get the account information on blockchain
@ -679,9 +436,9 @@ paths:
properties:
base_req:
$ref: "#/definitions/BaseReq"
delegator_addr:
delegator_address:
$ref: "#/definitions/Address"
validator_addr:
validator_address:
$ref: "#/definitions/ValidatorAddress"
delegation:
$ref: "#/definitions/Coin"
@ -764,9 +521,9 @@ paths:
properties:
base_req:
$ref: "#/definitions/BaseReq"
delegator_addr:
delegator_address:
$ref: "#/definitions/Address"
validator_addr:
validator_address:
$ref: "#/definitions/ValidatorAddress"
shares:
type: string
@ -861,17 +618,17 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: The sender and tx information
schema:
type: object
properties:
base_req:
$ref: "#/definitions/BaseReq"
delegator_addr:
delegator_address:
$ref: "#/definitions/Address"
validator_src_addr:
validator_src_addressess:
$ref: "#/definitions/ValidatorAddress"
validator_dst_addr:
validator_dst_address:
$ref: "#/definitions/ValidatorAddress"
shares:
type: string
@ -884,13 +641,11 @@ paths:
- application/json
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid delegator address or redelegation request body
401:
description: Key password is wrong
500:
description: Internal Server Error
/staking/delegators/{delegatorAddr}/validators:
@ -1170,16 +925,14 @@ paths:
type: object
properties:
base_req:
$ref: "#/definitions/BaseReq"
$ref: "#/definitions/StdTx"
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: Invalid validator address or base_req
401:
description: Key password is wrong
500:
description: Internal Server Error
/slashing/parameters:
@ -1246,13 +999,11 @@ paths:
$ref: "#/definitions/Coin"
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid proposal body
401:
description: Key password is wrong
500:
description: Internal Server Error
get:
@ -1807,6 +1558,28 @@ paths:
description: Invalid validator address
500:
description: Internal Server Error
/distribution/validators/{validatorAddr}/outstanding_rewards:
parameters:
- in: path
name: validatorAddr
description: Bech32 OperatorAddress of validator
required: true
type: string
get:
summary: Fee distribution outstanding rewards of a single validator
tags:
- ICS24
produces:
- application/json
responses:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/Coin"
500:
description: Internal Server Error
/distribution/validators/{validatorAddr}/rewards:
parameters:
- in: path
@ -1815,8 +1588,8 @@ paths:
required: true
type: string
get:
summary: Commission and self-delegation rewards of a single a validator
description: Query the commission and self-delegation rewards of a validator.
summary: Commission and self-delegation rewards of a single validator
description: Query the commission and self-delegation rewards of validator.
tags:
- ICS24
produces:
@ -1879,22 +1652,6 @@ paths:
type: string
500:
description: Internal Server Error
/distribution/outstanding_rewards:
get:
summary: Fee distribution outstanding rewards
tags:
- ICS24
produces:
- application/json
responses:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/Coin"
500:
description: Internal Server Error
definitions:
CheckTxResult:
type: object
@ -2189,9 +1946,6 @@ definitions:
type: string
example: "cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc"
description: Sender address or Keybase name to generate a transaction
password:
type: string
example: "12345678"
memo:
type: string
example: "Sent via Cosmos Voyager 🚀"
@ -2214,10 +1968,6 @@ definitions:
type: array
items:
$ref: "#/definitions/Coin"
generate_only:
type: boolean
example: false
description: Create a JSON transaction that can be signed client side instead of actually signing and broadcasting
simulate:
type: boolean
example: false
@ -2358,9 +2108,9 @@ definitions:
Delegation:
type: object
properties:
delegator_addr:
delegator_address:
type: string
validator_addr:
validator_address:
type: string
shares:
type: string
@ -2369,9 +2119,9 @@ definitions:
UnbondingDelegation:
type: object
properties:
delegator_addr:
delegator_address:
type: string
validator_addr:
validator_address:
type: string
initial_balance:
type: string
@ -2384,11 +2134,11 @@ definitions:
Redelegation:
type: object
properties:
delegator_addr:
delegator_address:
type: string
validator_src_addr:
validator_src_address:
type: string
validator_dst_addr:
validator_dst_address:
type: string
creation_height:
type: integer
@ -2405,7 +2155,7 @@ definitions:
ValidatorDistInfo:
type: object
properties:
operator_addr:
operator_address:
$ref: "#/definitions/ValidatorAddress"
self_bond_rewards:
type: array

View File

@ -19,10 +19,13 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
@ -397,7 +400,6 @@ func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing
// NOTE: If making updates here also update cmd/gaia/cmd/gaiacli/main.go
func registerRoutes(rs *RestServer) {
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
authrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey)
@ -557,7 +559,7 @@ func getKeys(t *testing.T, port string) []keys.KeyOutput {
// POST /keys Create a new account locally
func doKeysPost(t *testing.T, port, name, password, mnemonic string, account int, index int) keys.KeyOutput {
pk := keys.AddNewKey{name, password, mnemonic, account, index}
pk := clientkeys.AddNewKey{name, password, mnemonic, account, index}
req, err := cdc.MarshalJSON(pk)
require.NoError(t, err)
@ -583,7 +585,7 @@ func getKeysSeed(t *testing.T, port string) string {
// POST /keys/{name}/recove Recover a account from a seed
func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, mnemonic string, account uint32, index uint32) {
pk := keys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)}
pk := clientkeys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)}
req, err := cdc.MarshalJSON(pk)
require.NoError(t, err)
@ -611,7 +613,7 @@ func getKey(t *testing.T, port, name string) keys.KeyOutput {
// PUT /keys/{name} Update the password for this account in the KMS
func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) {
kr := keys.UpdateKeyReq{oldPassword, newPassword}
kr := clientkeys.UpdateKeyReq{oldPassword, newPassword}
req, err := cdc.MarshalJSON(kr)
require.NoError(t, err)
keyEndpoint := fmt.Sprintf("/keys/%s", name)
@ -625,7 +627,7 @@ func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail b
// DELETE /keys/{name} Remove an account
func deleteKey(t *testing.T, port, name, password string) {
dk := keys.DeleteKeyReq{password}
dk := clientkeys.DeleteKeyReq{password}
req, err := cdc.MarshalJSON(dk)
require.NoError(t, err)
keyEndpoint := fmt.Sprintf("/keys/%s", name)
@ -647,52 +649,42 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
// ICS 20 - Tokens
// ----------------------------------------------------------------------
// POST /tx/sign Sign a Tx
func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence uint64, msg auth.StdTx) auth.StdTx {
var signedMsg auth.StdTx
payload := authrest.SignBody{
Tx: msg,
BaseReq: rest.NewBaseReq(
name, password, "", chainID, "", "", accnum, sequence, nil, nil, false, false,
),
}
json, err := cdc.MarshalJSON(payload)
require.Nil(t, err)
res, body := Request(t, port, "POST", "/tx/sign", json)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg))
return signedMsg
}
// POST /tx/broadcast Send a signed Tx
func doBroadcast(t *testing.T, port string, msg auth.StdTx) sdk.TxResponse {
tx := authrest.BroadcastReq{Tx: msg, Return: "block"}
req, err := cdc.MarshalJSON(tx)
require.Nil(t, err)
res, body := Request(t, port, "POST", "/tx/broadcast", req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var resultTx sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
return resultTx
}
func doBroadcast(t *testing.T, port string, tx auth.StdTx) (*http.Response, string) {
txReq := clienttx.BroadcastReq{Tx: tx, Return: "block"}
// GET /bank/balances/{address} Get the account balances
// POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send)
func doTransfer(t *testing.T, port, seed, name, memo, password string, addr sdk.AccAddress, fees sdk.Coins) (receiveAddr sdk.AccAddress, resultTx sdk.TxResponse) {
res, body, receiveAddr := doTransferWithGas(t, port, seed, name, memo, password, addr, "", 1.0, false, false, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &resultTx)
req, err := cdc.MarshalJSON(txReq)
require.Nil(t, err)
return receiveAddr, resultTx
return Request(t, port, "POST", "/txs", req)
}
// doTransfer performs a balance transfer with auto gas calculation. It also signs
// the tx and broadcasts it.
func doTransfer(
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress, fees sdk.Coins,
) (sdk.AccAddress, sdk.TxResponse) {
resp, body, recvAddr := doTransferWithGas(
t, port, seed, name, memo, pwd, addr, "", 1.0, false, true, fees,
)
require.Equal(t, http.StatusOK, resp.StatusCode, resp)
var txResp sdk.TxResponse
err := cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return recvAddr, txResp
}
// doTransferWithGas performs a balance transfer with a specified gas value. The
// broadcast parameter determines if the tx should only be generated or also
// signed and broadcasted. The sending account's number and sequence are
// determined prior to generating the tx.
func doTransferWithGas(
t *testing.T, port, seed, from, memo, password string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins,
) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, broadcast bool, fees sdk.Coins,
) (resp *http.Response, body string, receiveAddr sdk.AccAddress) {
// create receive address
kb := crkeys.NewInMemory()
@ -708,15 +700,9 @@ func doTransferWithGas(
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
if generateOnly {
// generate only txs do not use a Keybase so the address must be used
from = addr.String()
}
from := addr.String()
baseReq := rest.NewBaseReq(
from, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil,
generateOnly, simulate,
from, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil, simulate,
)
sr := bankrest.SendReq{
@ -727,17 +713,28 @@ func doTransferWithGas(
req, err := cdc.MarshalJSON(sr)
require.NoError(t, err)
res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
return
// generate tx
resp, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
if !broadcast {
return resp, body, receiveAddr
}
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, gasAdjustment, simulate)
return resp, body, receiveAddr
}
// doTransferWithGasAccAuto is similar to doTransferWithGas except that it
// automatically determines the account's number and sequence when generating the
// tx.
func doTransferWithGasAccAuto(
t *testing.T, port, seed, from, memo, password string, gas string,
gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins,
) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, broadcast bool, fees sdk.Coins,
) (resp *http.Response, body string, receiveAddr sdk.AccAddress) {
// create receive address
kb := crkeys.NewInMemory()
acc := getAccount(t, port, addr)
receiveInfo, _, err := kb.CreateMnemonic(
"receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"),
@ -747,9 +744,9 @@ func doTransferWithGasAccAuto(
receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address())
chainID := viper.GetString(client.FlagChainID)
from := addr.String()
baseReq := rest.NewBaseReq(
from, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, generateOnly, simulate,
from, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, simulate,
)
sr := bankrest.SendReq{
@ -760,8 +757,45 @@ func doTransferWithGasAccAuto(
req, err := cdc.MarshalJSON(sr)
require.NoError(t, err)
res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
return
resp, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
if !broadcast {
return resp, body, receiveAddr
}
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, gasAdjustment, simulate)
return resp, body, receiveAddr
}
// signAndBroadcastGenTx accepts a successfully generated unsigned tx, signs it,
// and broadcasts it.
func signAndBroadcastGenTx(
t *testing.T, port, name, pwd, genTx string, acc auth.Account, gasAdjustment float64, simulate bool,
) (resp *http.Response, body string) {
chainID := viper.GetString(client.FlagChainID)
var tx auth.StdTx
err := cdc.UnmarshalJSON([]byte(genTx), &tx)
require.Nil(t, err)
txbldr := txbuilder.NewTxBuilder(
utils.GetTxEncoder(cdc),
acc.GetAccountNumber(),
acc.GetSequence(),
tx.Fee.Gas,
gasAdjustment,
simulate,
chainID,
tx.Memo,
tx.Fee.Amount,
nil,
)
signedTx, err := txbldr.SignStdTx(name, pwd, tx, false)
require.NoError(t, err)
return doBroadcast(t, port, signedTx)
}
// ----------------------------------------------------------------------
@ -769,112 +803,135 @@ func doTransferWithGasAccAuto(
// ----------------------------------------------------------------------
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doDelegate(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doDelegate(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress,
valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgDelegationsInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.NewCoin(sdk.DefaultBondDenom, amount),
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
Delegation: sdk.NewCoin(sdk.DefaultBondDenom, amount),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/delegations", delAddr.String()), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/delegations", delAddr.String()), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgDelegationsInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32
Delegation sdk.Coin `json:"delegation"`
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
Delegation sdk.Coin `json:"delegation"`
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doUndelegate(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doUndelegate(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress,
valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgUndelegateInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
SharesAmount: sdk.NewDecFromInt(amount),
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
SharesAmount: amount.ToDec(),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations", delAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations", delAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgUndelegateInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doBeginRedelegation(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount sdk.Int,
fees sdk.Coins) (resultTx sdk.TxResponse) {
func doBeginRedelegation(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress, valSrcAddr,
valDstAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := stakingrest.MsgBeginRedelegateInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorSrcAddr: valSrcAddr,
ValidatorDstAddr: valDstAddr,
SharesAmount: sdk.NewDecFromInt(amount),
BaseReq: baseReq,
DelegatorAddress: delAddr,
ValidatorSrcAddress: valSrcAddr,
ValidatorDstAddress: valDstAddr,
SharesAmount: amount.ToDec(),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/redelegations", delAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/redelegations", delAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgBeginRedelegateInput struct {
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32
ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
BaseReq rest.BaseReq `json:"base_req"`
DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32
ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` // in bech32
ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
}
// GET /staking/delegators/{delegatorAddr}/delegations Get all delegations from a delegator
@ -1073,15 +1130,18 @@ func getStakingParams(t *testing.T, port string) staking.Params {
// ICS 22 - Gov
// ----------------------------------------------------------------------
// POST /gov/proposals Submit a proposal
func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress,
amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doSubmitProposal(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
pr := govrest.PostProposalReq{
Title: "Test",
Description: "test",
@ -1095,14 +1155,17 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
require.NoError(t, err)
// submitproposal
res, body := Request(t, port, "POST", "/gov/proposals", req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", "/gov/proposals", req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals Query proposals
@ -1161,15 +1224,18 @@ func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStat
}
// POST /gov/proposals/{proposalId}/deposits Deposit tokens to a proposal
func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64,
amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doDeposit(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
proposalID uint64, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
dr := govrest.DepositReq{
Depositor: proposerAddr,
Amount: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, amount)},
@ -1179,14 +1245,17 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
req, err := cdc.MarshalJSON(dr)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals/{proposalId}/deposits Query deposits
@ -1210,14 +1279,19 @@ func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult {
}
// POST /gov/proposals/{proposalId}/votes Vote a proposal
func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, option string, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doVote(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
proposalID uint64, option string, fees sdk.Coins,
) sdk.TxResponse {
// get the account to get the sequence
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
vr := govrest.VoteReq{
Voter: proposerAddr,
Option: option,
@ -1227,14 +1301,17 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac
req, err := cdc.MarshalJSON(vr)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals/{proposalId}/votes Query voters
@ -1339,24 +1416,32 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.
// TODO: Test this functionality, it is not currently in any of the tests
// POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator
func doUnjail(t *testing.T, port, seed, name, password string,
valAddr sdk.ValAddress, fees sdk.Coins) (resultTx sdk.TxResponse) {
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false)
func doUnjail(
t *testing.T, port, seed, name, pwd string, valAddr sdk.ValAddress, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, sdk.AccAddress(valAddr.Bytes()))
from := acc.GetAddress().String()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(from, "", chainID, "", "", 1, 1, fees, nil, false)
ur := slashingrest.UnjailReq{
BaseReq: baseReq,
}
req, err := cdc.MarshalJSON(ur)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type unjailReq struct {
@ -1366,27 +1451,34 @@ type unjailReq struct {
// ICS24 - fee distribution
// POST /distribution/delegators/{delgatorAddr}/rewards Withdraw delegator rewards
func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password string,
delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doWithdrawDelegatorAllRewards(
t *testing.T, port, seed, name, pwd string, delegatorAddr sdk.AccAddress, fees sdk.Coins,
) sdk.TxResponse {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
wr := struct {
BaseReq rest.BaseReq `json:"base_req"`
}{BaseReq: baseReq}
req := cdc.MustMarshalJSON(wr)
res, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results sdk.TxResponse
cdc.MustUnmarshalJSON([]byte(body), &results)
resp, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err := cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
func mustParseDecCoins(dcstring string) sdk.DecCoins {

View File

@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -17,84 +16,6 @@ import (
//-----------------------------------------------------------------------------
// Building / Sending utilities
// CompleteAndBroadcastTxREST implements a utility function that facilitates
// sending a series of messages in a signed tx. In addition, it will handle
// tx gas simulation and estimation.
//
// NOTE: Also see CompleteAndBroadcastTxCLI.
func CompleteAndBroadcastTxREST(w http.ResponseWriter, cliCtx context.CLIContext,
baseReq rest.BaseReq, msgs []sdk.Msg, cdc *codec.Codec) {
gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
simAndExec, gas, err := client.ParseGas(baseReq.Gas)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(baseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
txBldr := authtxb.NewTxBuilder(
utils.GetTxEncoder(cdc), baseReq.AccountNumber,
baseReq.Sequence, gas, gasAdj, baseReq.Simulate,
baseReq.ChainID, baseReq.Memo, baseReq.Fees, baseReq.GasPrices,
)
txBldr, err = utils.PrepareTxBuilder(txBldr, cliCtx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
if baseReq.Simulate || simAndExec {
if gasAdj < 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error())
return
}
txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, msgs)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if baseReq.Simulate {
rest.WriteSimulationResponse(w, cdc, txBldr.Gas())
return
}
}
txBytes, err := txBldr.BuildAndSign(cliCtx.GetFromName(), baseReq.Password, msgs)
if keyerror.IsErrKeyNotFound(err) {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
} else if keyerror.IsErrWrongPassword(err) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
} else if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
// WriteGenerateStdTxResponse writes response for the generate only mode.
func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
cliCtx context.CLIContext, br rest.BaseReq, msgs []sdk.Msg) {
@ -115,7 +36,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices,
)
if simAndExec {
if br.Simulate || simAndExec {
if gasAdj < 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error())
return
@ -126,6 +47,11 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if br.Simulate {
rest.WriteSimulationResponse(w, cdc, txBldr.Gas())
return
}
}
stdMsg, err := txBldr.BuildSignMsg(msgs)

View File

@ -2,12 +2,19 @@ package tx
import (
"net/http"
"strings"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
"io/ioutil"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
)
@ -20,44 +27,93 @@ const (
flagBlock = "block"
)
// BroadcastBody Tx Broadcast Body
type BroadcastBody struct {
TxBytes []byte `json:"tx"`
Return string `json:"return"`
// BroadcastReq defines a tx broadcasting request.
type BroadcastReq struct {
Tx auth.StdTx `json:"tx"`
Return string `json:"return"`
}
// BroadcastTxRequest REST Handler
// nolint: gocyclo
// BroadcastTxRequest implements a tx broadcasting handler that is responsible
// for broadcasting a valid and signed tx to a full node. The tx can be
// broadcasted via a sync|async|block mechanism.
func BroadcastTxRequest(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m BroadcastBody
var req BroadcastReq
body, err := ioutil.ReadAll(r.Body)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = cdc.UnmarshalJSON(body, &m)
err = cdc.UnmarshalJSON(body, &req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
txBytes, err := cdc.MarshalBinaryLengthPrefixed(req.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var res interface{}
switch m.Return {
switch req.Return {
case flagBlock:
res, err = cliCtx.BroadcastTx(m.TxBytes)
res, err = cliCtx.BroadcastTx(txBytes)
case flagSync:
res, err = cliCtx.BroadcastTxSync(m.TxBytes)
res, err = cliCtx.BroadcastTxSync(txBytes)
case flagAsync:
res, err = cliCtx.BroadcastTxAsync(m.TxBytes)
res, err = cliCtx.BroadcastTxAsync(txBytes)
default:
rest.WriteErrorResponse(w, http.StatusInternalServerError,
"unsupported return type. supported types: block, sync, async")
return
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
// GetBroadcastCommand returns the tx broadcast command.
func GetBroadcastCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "broadcast [file_path]",
Short: "Broadcast transactions generated offline",
Long: strings.TrimSpace(`Broadcast transactions created with the --generate-only
flag and signed with the sign command. Read a transaction from [file_path] and
broadcast it to a node. If you supply a dash (-) argument in place of an input
filename, the command reads from standard input.
$ gaiacli tx broadcast ./mytxn.json
`),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return
}
res, err := cliCtx.BroadcastTx(txBytes)
cliCtx.PrintOutput(res)
return err
},
}
return client.PostCommands(cmd)[0]
}

107
client/tx/encode.go Normal file
View File

@ -0,0 +1,107 @@
package tx
import (
"encoding/base64"
"io/ioutil"
"net/http"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
)
type (
// EncodeReq defines a tx encoding request.
EncodeReq struct {
Tx auth.StdTx `json:"tx"`
}
// EncodeResp defines a tx encoding response.
EncodeResp struct {
Tx string `json:"tx"`
}
)
// EncodeTxRequestHandlerFn returns the encode tx REST handler. In particular,
// it takes a json-formatted transaction, encodes it to the Amino wire protocol,
// and responds with base64-encoded bytes.
func EncodeTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req EncodeReq
body, err := ioutil.ReadAll(r.Body)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = cdc.UnmarshalJSON(body, &req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// re-encode it via the Amino wire protocol
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(req.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// base64 encode the encoded tx bytes
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
response := EncodeResp{Tx: txBytesBase64}
rest.PostProcessResponse(w, cdc, response, cliCtx.Indent)
}
}
// txEncodeRespStr implements a simple Stringer wrapper for a encoded tx.
type txEncodeRespStr string
func (txr txEncodeRespStr) String() string {
return string(txr)
}
// GetEncodeCommand returns the encode command to take a JSONified transaction and turn it into
// Amino-serialized bytes
func GetEncodeCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "encode [file]",
Short: "Encode transactions generated offline",
Long: `Encode transactions created with the --generate-only flag and signed with the sign command.
Read a transaction from <file>, serialize it to the Amino wire protocol, and output it as base64.
If you supply a dash (-) argument in place of an input filename, the command reads from standard input.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
// re-encode it via the Amino wire protocol
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return err
}
// base64 encode the encoded tx bytes
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
response := txEncodeRespStr(txBytesBase64)
cliCtx.PrintOutput(response)
return nil
},
}
return client.PostCommands(cmd)[0]
}

View File

@ -12,4 +12,5 @@ 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", BroadcastTxRequest(cliCtx, cdc)).Methods("POST")
r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
}

View File

@ -3,12 +3,13 @@ package utils
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/client/context"
@ -40,8 +41,6 @@ func GenerateOrBroadcastMsgs(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
// 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 {
@ -64,6 +63,22 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte
return nil
}
if !cliCtx.SkipConfirm {
stdSignMsg, err := txBldr.BuildSignMsg(msgs)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "%s\n\n", cliCtx.Codec.MustMarshalJSON(stdSignMsg))
buf := client.BufferStdin()
ok, err := client.GetConfirmation("confirm transaction before signing and broadcasting", buf)
if err != nil || !ok {
fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
return err
}
}
passphrase, err := keys.GetPassphrase(fromName)
if err != nil {
return err
@ -110,20 +125,27 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
// Don't perform online validation or lookups if offline is true.
func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) {
func PrintUnsignedStdTx(
txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool,
) (err error) {
var stdTx auth.StdTx
if offline {
stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
} else {
stdTx, err = buildUnsignedStdTx(txBldr, cliCtx, msgs)
}
if err != nil {
return
}
json, err := cliCtx.Codec.MarshalJSON(stdTx)
if err == nil {
fmt.Fprintf(cliCtx.Output, "%s\n", json)
}
return
}
@ -190,6 +212,23 @@ func SignStdTxWithSignerAddress(txBldr authtxb.TxBuilder, cliCtx context.CLICont
return txBldr.SignStdTx(name, passphrase, stdTx, false)
}
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
func ReadStdTxFromFile(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) {
var bytes []byte
if filename == "-" {
bytes, err = ioutil.ReadAll(os.Stdin)
} else {
bytes, err = ioutil.ReadFile(filename)
}
if err != nil {
return
}
if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil {
return
}
return
}
func populateAccountFromState(txBldr authtxb.TxBuilder, cliCtx context.CLIContext,
addr sdk.AccAddress) (authtxb.TxBuilder, error) {
if txBldr.AccountNumber() == 0 {

View File

@ -3,19 +3,19 @@ package utils
import (
"encoding/json"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
var (
@ -110,3 +110,32 @@ func makeCodec() *codec.Codec {
cdc.RegisterConcrete(sdk.TestMsg{}, "cosmos-sdk/Test", nil)
return cdc
}
func TestReadStdTxFromFile(t *testing.T) {
cdc := codec.New()
sdk.RegisterCodec(cdc)
// Build a test transaction
fee := auth.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)})
stdTx := auth.NewStdTx([]sdk.Msg{}, fee, []auth.StdSignature{}, "foomemo")
// Write it to the file
encodedTx, _ := cdc.MarshalJSON(stdTx)
jsonTxFile := writeToNewTempFile(t, string(encodedTx))
defer os.Remove(jsonTxFile.Name())
// Read it back
decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name())
require.Nil(t, err)
require.Equal(t, decodedTx.Memo, "foomemo")
}
func writeToNewTempFile(t *testing.T, data string) *os.File {
fp, err := ioutil.TempFile(os.TempDir(), "client_tx_test")
require.Nil(t, err)
_, err = fp.WriteString(data)
require.Nil(t, err)
return fp
}

View File

@ -11,6 +11,9 @@ import (
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
// TODO: Remove once transfers are enabled.
gaiabank "github.com/cosmos/cosmos-sdk/cmd/gaia/app/x/bank"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -149,14 +152,17 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
)
// register message routes
//
// TODO: Use standard bank router once transfers are enabled.
app.Router().
AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)).
AddRoute(bank.RouterKey, gaiabank.NewHandler(app.bankKeeper)).
AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)).
AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)).
AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)).
AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper))
app.QueryRouter().
AddRoute(auth.QuerierRoute, auth.NewQuerier(app.accountKeeper)).
AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)).
AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)).
AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)).
@ -324,7 +330,7 @@ func (app *GaiaApp) LoadHeight(height int64) error {
return app.LoadVersion(height, app.keyMain)
}
//______________________________________________________________________________________________
// ______________________________________________________________________________________________
var _ sdk.StakingHooks = StakingHooks{}

View File

@ -89,7 +89,7 @@ func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st
// withdraw all delegator rewards
dels := app.stakingKeeper.GetAllDelegations(ctx)
for _, delegation := range dels {
_ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
_ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
}
// clear validator slash events
@ -104,13 +104,20 @@ func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []st
// reinitialize all validators
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) {
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
feePool := app.distrKeeper.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(scraps)
app.distrKeeper.SetFeePool(ctx, feePool)
app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
return false
})
// reinitialize all delegations
for _, del := range dels {
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddr, del.ValidatorAddr)
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
}
// reset context height

View File

@ -11,13 +11,12 @@ import (
"strings"
"time"
"github.com/cosmos/cosmos-sdk/x/bank"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
@ -360,8 +359,8 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm
msg := msgs[0].(staking.MsgCreateValidator)
// validate delegator and validator addresses and funds against the accounts in the state
delAddr := msg.DelegatorAddr.String()
valAddr := sdk.AccAddress(msg.ValidatorAddr).String()
delAddr := msg.DelegatorAddress.String()
valAddr := sdk.AccAddress(msg.ValidatorAddress).String()
delAcc, delOk := addrMap[delAddr]
_, valOk := addrMap[valAddr]

View File

@ -40,7 +40,7 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
require.Equal(t, 1, len(msgs))
msg := msgs[0].(staking.MsgCreateValidator)
acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr))
acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddress))
acc.Coins = sdk.Coins{sdk.NewInt64Coin(defaultBondDenom, 150)}
genAccs[i] = NewGenesisAccount(&acc)
stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.Add(sdk.NewInt(150)) // increase the supply

View File

@ -161,9 +161,9 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest
VotingPeriod: vp,
},
TallyParams: gov.TallyParams{
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
GovernancePenalty: sdk.NewDecWithPrec(1, 2),
Quorum: sdk.NewDecWithPrec(334, 3),
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
},
}
fmt.Printf("Selected randomly generated governance parameters:\n\t%+v\n", govGenesis)
@ -172,7 +172,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest
Pool: staking.InitialPool(),
Params: staking.Params{
UnbondingTime: time.Duration(randIntBetween(r, 60, 60*60*24*3*2)) * time.Second,
MaxValidators: uint16(r.Intn(250)),
MaxValidators: uint16(r.Intn(250) + 1),
BondDenom: sdk.DefaultBondDenom,
},
}
@ -305,7 +305,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
db, _ = sdk.NewLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
@ -347,7 +347,7 @@ func TestFullGaiaSimulation(t *testing.T) {
}
var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
db, _ = sdk.NewLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
@ -388,7 +388,7 @@ func TestGaiaImportExport(t *testing.T) {
}
var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
db, _ = sdk.NewLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
@ -421,7 +421,7 @@ func TestGaiaImportExport(t *testing.T) {
fmt.Printf("Importing genesis...\n")
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir)
newDB, _ := sdk.NewLevelDB("Simulation-2", dir)
defer func() {
newDB.Close()
os.RemoveAll(newDir)
@ -483,7 +483,7 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
logger = log.NewNopLogger()
}
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ := dbm.NewGoLevelDB("Simulation", dir)
db, _ := sdk.NewLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
@ -525,7 +525,7 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
fmt.Printf("Importing genesis...\n")
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir)
newDB, _ := sdk.NewLevelDB("Simulation-2", dir)
defer func() {
newDB.Close()
os.RemoveAll(newDir)

View File

@ -0,0 +1,100 @@
// Package bank contains a forked version of the bank module. It only contains
// a modified message handler to support a very limited form of transfers during
// mainnet launch -- MsgMultiSend messages.
//
// NOTE: This fork should be removed entirely once transfers are enabled and
// the Gaia router should be reset to using the original bank module handler.
package bank
import (
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
)
var (
uatomDenom = "uatom"
atomsToUatoms = int64(1000000)
// BurnedCoinsAccAddr represents the burn account address used for
// MsgMultiSend message during the period for which transfers are disabled.
// Its Bech32 address is cosmos1x4p90uuy63fqzsheamn48vq88q3eusykf0a69v.
BurnedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("bankBurnedCoins")))
)
// NewHandler returns a handler for "bank" type messages.
func NewHandler(k bank.Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case bank.MsgSend:
return handleMsgSend(ctx, k, msg)
case bank.MsgMultiSend:
return handleMsgMultiSend(ctx, k, msg)
default:
errMsg := "Unrecognized bank Msg type: %s" + msg.Type()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// handleMsgSend implements a MsgSend message handler. It operates no differently
// than the standard bank module MsgSend message handler in that it transfers
// an amount from one account to another under the condition of transfers being
// enabled.
func handleMsgSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgSend) sdk.Result {
// No need to modify handleMsgSend as the forked module requires no changes,
// so we can just call the standard bank modules handleMsgSend since we know
// the message is of type MsgSend.
return bank.NewHandler(k)(ctx, msg)
}
// handleMsgMultiSend implements a modified forked version of a MsgMultiSend
// message handler. If transfers are disabled, a modified version of MsgMultiSend
// is allowed where there must be a single input and only two outputs. The first
// of the two outputs must be to a specific burn address defined by
// burnedCoinsAccAddr. In addition, the output amounts must be of 9atom and
// 1uatom respectively.
func handleMsgMultiSend(ctx sdk.Context, k bank.Keeper, msg bank.MsgMultiSend) sdk.Result {
// NOTE: totalIn == totalOut should already have been checked
if !k.GetSendEnabled(ctx) {
if !validateMultiSendTransfersDisabled(msg) {
return bank.ErrSendDisabled(k.Codespace()).Result()
}
}
tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs)
if err != nil {
return err.Result()
}
return sdk.Result{
Tags: tags,
}
}
func validateMultiSendTransfersDisabled(msg bank.MsgMultiSend) bool {
nineAtoms := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 9*atomsToUatoms)}
oneAtom := sdk.Coins{sdk.NewInt64Coin(uatomDenom, 1*atomsToUatoms)}
if len(msg.Inputs) != 1 {
return false
}
if len(msg.Outputs) != 2 {
return false
}
if !msg.Outputs[0].Address.Equals(BurnedCoinsAccAddr) {
return false
}
if !msg.Outputs[0].Coins.IsEqual(nineAtoms) {
return false
}
if !msg.Outputs[1].Coins.IsEqual(oneAtom) {
return false
}
return true
}

View File

@ -0,0 +1,216 @@
package bank
import (
"testing"
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/secp256k1"
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/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
)
var (
addrs = []sdk.AccAddress{
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()),
}
initAmt = sdk.NewInt(atomsToUatoms * 100)
)
type testInput struct {
ctx sdk.Context
accKeeper auth.AccountKeeper
bankKeeper bank.Keeper
}
func newTestCodec() *codec.Codec {
cdc := codec.New()
bank.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
}
func createTestInput(t *testing.T) testInput {
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
cdc := newTestCodec()
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger())
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
require.NoError(t, ms.LoadLatestVersion())
paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams)
accKeeper := auth.NewAccountKeeper(
cdc,
keyAcc,
paramsKeeper.Subspace(auth.DefaultParamspace),
auth.ProtoBaseAccount,
)
bankKeeper := bank.NewBaseKeeper(
accKeeper,
paramsKeeper.Subspace(bank.DefaultParamspace),
bank.DefaultCodespace,
)
for _, addr := range addrs {
_, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
require.NoError(t, err)
}
return testInput{ctx, accKeeper, bankKeeper}
}
func TestHandlerMsgSendTransfersDisabled(t *testing.T) {
input := createTestInput(t)
input.bankKeeper.SetSendEnabled(input.ctx, false)
handler := NewHandler(input.bankKeeper)
amt := sdk.NewInt(atomsToUatoms * 5)
msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)})
res := handler(input.ctx, msg)
require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log)
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
}
func TestHandlerMsgSendTransfersEnabled(t *testing.T) {
input := createTestInput(t)
input.bankKeeper.SetSendEnabled(input.ctx, true)
handler := NewHandler(input.bankKeeper)
amt := sdk.NewInt(atomsToUatoms * 5)
msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, amt)})
res := handler(input.ctx, msg)
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
balance := initAmt.Sub(amt)
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
balance = initAmt.Add(amt)
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
}
func TestHandlerMsgMultiSendTransfersDisabledBurn(t *testing.T) {
input := createTestInput(t)
input.bankKeeper.SetSendEnabled(input.ctx, false)
handler := NewHandler(input.bankKeeper)
totalAmt := sdk.NewInt(10 * atomsToUatoms)
burnAmt := sdk.NewInt(9 * atomsToUatoms)
transAmt := sdk.NewInt(1 * atomsToUatoms)
msg := bank.NewMsgMultiSend(
[]bank.Input{
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
},
[]bank.Output{
bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}),
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}),
},
)
res := handler(input.ctx, msg)
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
balance := initAmt.Sub(totalAmt)
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
balance = initAmt.Add(transAmt)
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
burn := input.accKeeper.GetAccount(input.ctx, BurnedCoinsAccAddr)
require.Equal(t, burn.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)})
}
func TestHandlerMsgMultiSendTransfersDisabledInvalidBurn(t *testing.T) {
input := createTestInput(t)
input.bankKeeper.SetSendEnabled(input.ctx, false)
handler := NewHandler(input.bankKeeper)
totalAmt := sdk.NewInt(15 * atomsToUatoms)
burnAmt := sdk.NewInt(10 * atomsToUatoms)
transAmt := sdk.NewInt(5 * atomsToUatoms)
msg := bank.NewMsgMultiSend(
[]bank.Input{
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
},
[]bank.Output{
bank.NewOutput(BurnedCoinsAccAddr, sdk.Coins{sdk.NewCoin(uatomDenom, burnAmt)}),
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, transAmt)}),
},
)
res := handler(input.ctx, msg)
require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log)
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
to := input.accKeeper.GetAccount(input.ctx, addrs[1])
require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, initAmt)})
}
func TestHandlerMsgMultiSendTransfersEnabled(t *testing.T) {
input := createTestInput(t)
input.bankKeeper.SetSendEnabled(input.ctx, true)
handler := NewHandler(input.bankKeeper)
totalAmt := sdk.NewInt(10 * atomsToUatoms)
outAmt := sdk.NewInt(5 * atomsToUatoms)
msg := bank.NewMsgMultiSend(
[]bank.Input{
bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(uatomDenom, totalAmt)}),
},
[]bank.Output{
bank.NewOutput(addrs[1], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}),
bank.NewOutput(addrs[2], sdk.Coins{sdk.NewCoin(uatomDenom, outAmt)}),
},
)
res := handler(input.ctx, msg)
require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log)
from := input.accKeeper.GetAccount(input.ctx, addrs[0])
balance := initAmt.Sub(totalAmt)
require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
out1 := input.accKeeper.GetAccount(input.ctx, addrs[1])
balance = initAmt.Add(outAmt)
require.Equal(t, out1.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
out2 := input.accKeeper.GetAccount(input.ctx, addrs[2])
balance = initAmt.Add(outAmt)
require.Equal(t, out2.GetCoins(), sdk.Coins{sdk.NewCoin(uatomDenom, balance)})
}

View File

@ -86,19 +86,19 @@ func TestGaiaCLIMinimumFees(t *testing.T) {
barAddr := f.KeyAddress(keyBar)
// Send a transaction that will get rejected
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10))
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), "-y")
require.False(f.T, success)
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure tx w/ correct fees pass
txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2))
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), txFees)
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), txFees, "-y")
require.True(f.T, success)
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure tx w/ improper fees fails
txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 1))
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees)
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees, "-y")
require.False(f.T, success)
// Cleanup testing directories
@ -120,7 +120,7 @@ func TestGaiaCLIGasPrices(t *testing.T) {
badGasPrice, _ := sdk.NewDecFromStr("0.000003")
success, _, _ := f.TxSend(
keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50),
fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, badGasPrice)))
fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, badGasPrice)), "-y")
require.False(t, success)
// wait for a block confirmation
@ -129,7 +129,7 @@ func TestGaiaCLIGasPrices(t *testing.T) {
// sufficient gas prices (tx passes)
success, _, _ = f.TxSend(
keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50),
fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)))
fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)), "-y")
require.True(t, success)
// wait for a block confirmation
@ -171,7 +171,7 @@ func TestGaiaCLIFeesDeduction(t *testing.T) {
largeCoins := sdk.TokensFromTendermintPower(10000000)
success, _, _ = f.TxSend(
keyFoo, barAddr, sdk.NewCoin(fooDenom, largeCoins),
fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)))
fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)), "-y")
require.False(t, success)
// Wait for a block
@ -184,7 +184,7 @@ func TestGaiaCLIFeesDeduction(t *testing.T) {
// test success (transfer = coins + fees)
success, _, _ = f.TxSend(
keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 500),
fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)))
fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)), "-y")
require.True(t, success)
f.Cleanup()
@ -208,7 +208,7 @@ func TestGaiaCLISend(t *testing.T) {
// Send some tokens from one account to the other
sendTokens := sdk.TokensFromTendermintPower(10)
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens))
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure account balances match expected
@ -226,7 +226,7 @@ func TestGaiaCLISend(t *testing.T) {
require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom))
// test autosequencing
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens))
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure account balances match expected
@ -236,7 +236,7 @@ func TestGaiaCLISend(t *testing.T) {
require.Equal(t, startTokens.Sub(sendTokens.MulRaw(2)), fooAcc.GetCoins().AmountOf(denom))
// test memo
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--memo='testmemo'")
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--memo='testmemo'", "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure account balances match expected
@ -248,6 +248,40 @@ func TestGaiaCLISend(t *testing.T) {
f.Cleanup()
}
func TestGaiaCLIConfirmTx(t *testing.T) {
t.Parallel()
f := InitFixtures(t)
// start gaiad server
proc := f.GDStart()
defer proc.Stop(false)
// Save key addresses for later use
fooAddr := f.KeyAddress(keyFoo)
barAddr := f.KeyAddress(keyBar)
fooAcc := f.QueryAccount(fooAddr)
startTokens := sdk.TokensFromTendermintPower(50)
require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom))
// send some tokens from one account to the other
sendTokens := sdk.TokensFromTendermintPower(10)
f.txSendWithConfirm(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "Y")
tests.WaitForNextNBlocksTM(1, f.Port)
// ensure account balances match expected
barAcc := f.QueryAccount(barAddr)
require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom))
// send some tokens from one account to the other (cancelling confirmation)
f.txSendWithConfirm(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "n")
tests.WaitForNextNBlocksTM(1, f.Port)
// ensure account balances match expected
barAcc = f.QueryAccount(barAddr)
require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom))
}
func TestGaiaCLIGasAuto(t *testing.T) {
t.Parallel()
f := InitFixtures(t)
@ -265,7 +299,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
// Test failure with auto gas disabled and very little gas set by hand
sendTokens := sdk.TokensFromTendermintPower(10)
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=10")
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=10", "-y")
require.False(t, success)
// Check state didn't change
@ -273,7 +307,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom))
// Test failure with negative gas
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=-100")
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=-100", "-y")
require.False(t, success)
// Check state didn't change
@ -281,7 +315,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom))
// Test failure with 0 gas
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=0")
success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=0", "-y")
require.False(t, success)
// Check state didn't change
@ -289,7 +323,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom))
// Enable auto gas
success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto")
success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "-y")
require.NotEmpty(t, stderr)
require.True(t, success)
cdc := app.MakeCodec()
@ -320,7 +354,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
consPubKey := sdk.MustBech32ifyConsPub(ed25519.GenPrivKey().PubKey())
sendTokens := sdk.TokensFromTendermintPower(10)
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens))
f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
barAcc := f.QueryAccount(barAddr)
@ -342,7 +376,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
require.True(t, success)
// Create the validator
f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens))
f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure funds were deducted properly
@ -351,7 +385,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
// Ensure that validator state is as expected
validator := f.QueryStakingValidator(barVal)
require.Equal(t, validator.OperatorAddr, barVal)
require.Equal(t, validator.OperatorAddress, barVal)
require.True(sdk.IntEq(t, newValTokens, validator.Tokens))
// Query delegations to the validator
@ -361,7 +395,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
// unbond a single share
unbondTokens := sdk.TokensFromTendermintPower(1)
success = f.TxStakingUnbond(keyBar, unbondTokens.String(), barVal)
success = f.TxStakingUnbond(keyBar, unbondTokens.String(), barVal, "-y")
require.True(t, success)
tests.WaitForNextNBlocksTM(1, f.Port)
@ -403,7 +437,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
// Test submit generate only for submit proposal
proposalTokens := sdk.TokensFromTendermintPower(5)
success, stdout, stderr := f.TxGovSubmitProposal(
keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only")
keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only", "-y")
require.True(t, success)
require.Empty(t, stderr)
msg := unmarshalStdTx(t, stdout)
@ -416,7 +450,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.True(t, success)
// Create the proposal
f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens))
f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Ensure transaction tags can be queried
@ -451,7 +485,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, 0, len(msg.GetSignatures()))
// Run the deposit transaction
f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens))
f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// test query deposit
@ -486,7 +520,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, 0, len(msg.GetSignatures()))
// Vote on the proposal
f.TxGovVote(1, gov.OptionYes, keyFoo)
f.TxGovVote(1, gov.OptionYes, keyFoo, "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Query the vote
@ -513,7 +547,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, uint64(1), proposalsQuery[0].GetProposalID())
// submit a second test proposal
f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewCoin(denom, proposalTokens))
f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewCoin(denom, proposalTokens), "-y")
tests.WaitForNextNBlocksTM(1, f.Port)
// Test limit on proposals query
@ -538,7 +572,7 @@ func TestGaiaCLIQueryTxPagination(t *testing.T) {
seq := accFoo.GetSequence()
for i := 1; i <= 30; i++ {
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, int64(i)), fmt.Sprintf("--sequence=%d", seq))
success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, int64(i)), fmt.Sprintf("--sequence=%d", seq), "-y")
require.True(t, success)
seq++
}
@ -667,7 +701,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
// Test sign --validate-signatures
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--validate-signatures")
require.False(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
// Test sign
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name())
@ -684,7 +718,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
// Test sign --validate-signatures
success, stdout, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures")
require.True(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(),
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t\t\t[OK]\n\n", fooAddr.String(),
fooAddr.String()), stdout)
// Ensure foo has right amount of funds
@ -725,7 +759,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
barAddr := f.KeyAddress(keyBar)
// Send some tokens from one account to the other
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10))
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y")
require.True(t, success)
tests.WaitForNextNBlocksTM(1, f.Port)
@ -738,7 +772,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
defer os.Remove(unsignedTxFile.Name())
// Sign with foo's key
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
require.True(t, success)
// Write the output to disk
@ -810,7 +844,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
barAddr := f.KeyAddress(keyBar)
// Send some tokens from one account to the other
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10))
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y")
require.True(t, success)
tests.WaitForNextNBlocksTM(1, f.Port)
@ -872,7 +906,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
bazAddr := f.KeyAddress(keyBaz)
// Send some tokens from one account to the other
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10))
success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y")
require.True(t, success)
tests.WaitForNextNBlocksTM(1, f.Port)
@ -890,7 +924,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
defer os.Remove(unsignedTxFile.Name())
// Sign with foo's key
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
require.True(t, success)
// Write the output to disk
@ -898,7 +932,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
defer os.Remove(fooSignatureFile.Name())
// Sign with bar's key
success, stdout, _ = f.TxSign(keyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
success, stdout, _ = f.TxSign(keyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
require.True(t, success)
// Write the output to disk
@ -915,7 +949,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
defer os.Remove(signedTxFile.Name())
// Validate the multisignature
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures")
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "-y")
require.True(t, success)
// Broadcast the transaction

View File

@ -14,10 +14,11 @@ import (
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/client/keys"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/codec"
"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"
@ -70,10 +71,13 @@ type Fixtures struct {
func NewFixtures(t *testing.T) *Fixtures {
tmpDir, err := ioutil.TempDir("", "gaia_integration_"+t.Name()+"_")
require.NoError(t, err)
servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
p2pAddr, _, err := server.FreeTCPAddr()
require.NoError(t, err)
return &Fixtures{
T: t,
GDHome: filepath.Join(tmpDir, ".gaiad"),
@ -257,7 +261,7 @@ func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput {
cmd := fmt.Sprintf("gaiacli keys show --home=%s %s", f.GCLIHome, name)
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var ko keys.KeyOutput
err := keys.UnmarshalJSON([]byte(out), &ko)
err := clientkeys.UnmarshalJSON([]byte(out), &ko)
require.NoError(f.T, err)
return ko
}
@ -288,9 +292,17 @@ func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
func (f *Fixtures) txSendWithConfirm(
from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string,
) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, app.DefaultKeyPass)
}
// TxSign is gaiacli tx sign
func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx sign %v --name=%s %v", f.Flags(), signer, fileName)
cmd := fmt.Sprintf("gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}

View File

@ -141,8 +141,8 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
client.LineBreak,
authcmd.GetSignCommand(cdc),
authcmd.GetMultiSignCommand(cdc),
authcmd.GetBroadcastCommand(cdc),
authcmd.GetEncodeCommand(cdc),
tx.GetBroadcastCommand(cdc),
tx.GetEncodeCommand(cdc),
client.LineBreak,
)
@ -158,7 +158,6 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go
func registerRoutes(rs *lcd.RestServer) {
registerSwaggerUI(rs)
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey)

View File

@ -45,7 +45,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error {
// load the app
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
db, err := dbm.NewGoLevelDB("gaia", dataDir)
db, err := sdk.NewLevelDB("gaia", dataDir)
if err != nil {
fmt.Println(err)
os.Exit(1)

View File

@ -112,12 +112,17 @@ func runPubKeyCmd(cmd *cobra.Command, args []string) error {
pubKeyI, err4 = sdk.GetValPubKeyBech32(pubkeyString)
if err4 != nil {
return fmt.Errorf(`Expected hex, base64, or bech32. Got errors:
hex: %v,
base64: %v
bech32 acc: %v
bech32 val: %v
`, err, err2, err3, err4)
var err5 error
pubKeyI, err5 = sdk.GetConsPubKeyBech32(pubkeyString)
if err5 != nil {
return fmt.Errorf(`Expected hex, base64, or bech32. Got errors:
hex: %v,
base64: %v
bech32 Acc: %v
bech32 Val: %v
bech32 Cons: %v`,
err, err2, err3, err4, err5)
}
}
}

View File

@ -5,10 +5,18 @@ import (
"fmt"
"os"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/libs/bech32"
)
var bech32Prefixes = []string{"cosmos", "cosmospub", "cosmosvaloper", "cosmosvaloperpub", "cosmosvalcons", "cosmosvalconspub"}
var bech32Prefixes = []string{
sdk.Bech32PrefixAccAddr,
sdk.Bech32PrefixAccPub,
sdk.Bech32PrefixValAddr,
sdk.Bech32PrefixValPub,
sdk.Bech32PrefixConsAddr,
sdk.Bech32PrefixConsPub,
}
func main() {
if len(os.Args) < 2 {

View File

@ -15,7 +15,6 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
bcm "github.com/tendermint/tendermint/blockchain"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/proxy"
tmsm "github.com/tendermint/tendermint/state"
tm "github.com/tendermint/tendermint/types"
@ -23,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
@ -72,7 +72,7 @@ func run(rootDir string) {
// App DB
// appDB := dbm.NewMemDB()
fmt.Println("Opening app database")
appDB, err := dbm.NewGoLevelDB("application", dataDir)
appDB, err := sdk.NewLevelDB("application", dataDir)
if err != nil {
panic(err)
}
@ -80,14 +80,14 @@ func run(rootDir string) {
// TM DB
// tmDB := dbm.NewMemDB()
fmt.Println("Opening tendermint state database")
tmDB, err := dbm.NewGoLevelDB("state", dataDir)
tmDB, err := sdk.NewLevelDB("state", dataDir)
if err != nil {
panic(err)
}
// Blockchain DB
fmt.Println("Opening blockstore database")
bcDB, err := dbm.NewGoLevelDB("blockstore", dataDir)
bcDB, err := sdk.NewLevelDB("blockstore", dataDir)
if err != nil {
panic(err)
}

View File

@ -24,6 +24,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
kbkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -129,9 +130,20 @@ following delegation and commission default parameters:
return err
}
info, err := txBldr.Keybase().Get(name)
if err != nil {
return err
}
if info.GetType() == kbkeys.TypeOffline || info.GetType() == kbkeys.TypeMulti {
fmt.Println("Offline key passed in. Use `gaiacli tx sign` command to sign:")
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true)
}
// write the unsigned transaction to the buffer
w := bytes.NewBuffer([]byte{})
cliCtx = cliCtx.WithOutput(w)
if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil {
return err
}
@ -163,6 +175,7 @@ following delegation and commission default parameters:
fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument)
return nil
},
}

View File

@ -1,9 +1,10 @@
package keys
import (
amino "github.com/tendermint/go-amino"
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto/encoding/amino"
)
var cdc = amino.NewCodec()
@ -15,4 +16,5 @@ func init() {
cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil)
cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil)
cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil)
cdc.RegisterConcrete(multiInfo{}, "crypto/keys/multiInfo", nil)
}

View File

@ -7,7 +7,7 @@ import (
"reflect"
"strings"
"errors"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
@ -15,10 +15,10 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/go-bip39"
bip39 "github.com/cosmos/go-bip39"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding/amino"
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/crypto/secp256k1"
dbm "github.com/tendermint/tendermint/libs/db"
)
@ -152,12 +152,18 @@ func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, account uint32,
return kb.writeLedgerKey(name, pub, *hdPath), nil
}
// CreateOffline creates a new reference to an offline keypair
// It returns the created key info
// CreateOffline creates a new reference to an offline keypair. It returns the
// created key info.
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) {
return kb.writeOfflineKey(name, pub), nil
}
// CreateMulti creates a new reference to a multisig (offline) keypair. It
// returns the created key info.
func (kb dbKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) {
return kb.writeMultisigKey(name, pub), nil
}
func (kb *dbKeybase) persistDerivedKey(seed []byte, passwd, name, fullHdPath string) (info Info, err error) {
// create master key and derive first key:
masterPriv, ch := hd.ComputeMastersFromSeed(seed)
@ -222,7 +228,9 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
if err != nil {
return
}
var priv tmcrypto.PrivKey
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
@ -230,39 +238,49 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
err = fmt.Errorf("private key not available")
return
}
priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
case ledgerInfo:
linfo := info.(ledgerInfo)
priv, err = crypto.NewPrivKeyLedgerSecp256k1(linfo.Path)
if err != nil {
return
}
case offlineInfo:
linfo := info.(offlineInfo)
_, err := fmt.Fprintf(os.Stderr, "Bytes to sign:\n%s", msg)
case offlineInfo, multiInfo:
_, err := fmt.Fprintf(os.Stderr, "Message to sign:\n\n%s\n", msg)
if err != nil {
return nil, nil, err
}
buf := bufio.NewReader(os.Stdin)
_, err = fmt.Fprintf(os.Stderr, "\nEnter Amino-encoded signature:\n")
if err != nil {
return nil, nil, err
}
// Will block until user inputs the signature
signed, err := buf.ReadString('\n')
if err != nil {
return nil, nil, err
}
cdc.MustUnmarshalBinaryLengthPrefixed([]byte(signed), sig)
return sig, linfo.GetPubKey(), nil
if err := cdc.UnmarshalBinaryLengthPrefixed([]byte(signed), sig); err != nil {
return nil, nil, errors.Wrap(err, "failed to decode signature")
}
return sig, info.GetPubKey(), nil
}
sig, err = priv.Sign(msg)
if err != nil {
return nil, nil, err
}
pub = priv.PubKey()
return sig, pub, nil
}
@ -272,7 +290,9 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
if err != nil {
return nil, err
}
var priv tmcrypto.PrivKey
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
@ -284,11 +304,11 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
if err != nil {
return nil, err
}
case ledgerInfo:
return nil, errors.New("only works on local private keys")
case offlineInfo:
case ledgerInfo, offlineInfo, multiInfo:
return nil, errors.New("only works on local private keys")
}
return priv, nil
}
@ -426,6 +446,12 @@ func (kb dbKeybase) writeOfflineKey(name string, pub tmcrypto.PubKey) Info {
return info
}
func (kb dbKeybase) writeMultisigKey(name string, pub tmcrypto.PubKey) Info {
info := NewMultiInfo(name, pub)
kb.writeInfo(name, info)
return info
}
func (kb dbKeybase) writeInfo(name string, info Info) {
// write the info by key
key := infoKey(name)

View File

@ -74,9 +74,9 @@ func TestCreateLedger(t *testing.T) {
pk, err = sdk.Bech32ifyAccPub(pubKey)
assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk)
linfo := restoredKey.(ledgerInfo)
assert.Equal(t, "44'/118'/3'/0/1", linfo.GetPath().String())
path, err := restoredKey.GetPath()
assert.NoError(t, err)
assert.Equal(t, "44'/118'/3'/0/1", path.String())
}
// TestKeyManagement makes sure we can manipulate these keys well

View File

@ -1,11 +1,13 @@
package keys
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var _ Keybase = lazyKeybase{}
@ -17,150 +19,180 @@ type lazyKeybase struct {
// New creates a new instance of a lazy keybase.
func New(name, dir string) Keybase {
if err := cmn.EnsureDir(dir, 0700); err != nil {
panic(fmt.Sprintf("failed to create Keybase directory: %s", err))
}
return lazyKeybase{name: name, dir: dir}
}
func (lkb lazyKeybase) List() ([]Info, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).List()
}
func (lkb lazyKeybase) Get(name string) (Info, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).Get(name)
}
func (lkb lazyKeybase) GetByAddress(address types.AccAddress) (Info, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
func (lkb lazyKeybase) GetByAddress(address sdk.AccAddress) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).GetByAddress(address)
}
func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Delete(name, passphrase, skipPass)
}
func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, nil, err
}
defer db.Close()
return newDbKeybase(db).Sign(name, passphrase, msg)
}
func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, "", err
}
defer db.Close()
return newDbKeybase(db).CreateMnemonic(name, language, passwd, algo)
}
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index)
}
func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params)
}
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (info Info, err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateLedger(name, algo, account, index)
}
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateOffline(name, pubkey)
}
func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateMulti(name, pubkey)
}
func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Update(name, oldpass, getNewpass)
}
func (lkb lazyKeybase) Import(name string, armor string) (err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Import(name, armor)
}
func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).ImportPubKey(name, armor)
}
func (lkb lazyKeybase) Export(name string) (armor string, err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDbKeybase(db).Export(name)
}
func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDbKeybase(db).ExportPubKey(name)
}
func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) {
db, err := dbm.NewGoLevelDB(lkb.name, lkb.dir)
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).ExportPrivateKeyObject(name, passphrase)
}

111
crypto/keys/output.go Normal file
View File

@ -0,0 +1,111 @@
package keys
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// KeyOutput defines a structure wrapping around an Info object used for output
// functionality.
type KeyOutput struct {
Name string `json:"name"`
Type string `json:"type"`
Address string `json:"address"`
PubKey string `json:"pubkey"`
Mnemonic string `json:"mnemonic,omitempty"`
Threshold uint `json:"threshold,omitempty"`
PubKeys []multisigPubKeyOutput `json:"pubkeys,omitempty"`
}
type multisigPubKeyOutput struct {
Address string `json:"address"`
PubKey string `json:"pubkey"`
Weight uint `json:"weight"`
}
// Bech32KeysOutput returns a slice of KeyOutput objects, each with the "acc"
// Bech32 prefixes, given a slice of Info objects. It returns an error if any
// call to Bech32KeyOutput fails.
func Bech32KeysOutput(infos []Info) ([]KeyOutput, error) {
kos := make([]KeyOutput, len(infos))
for i, info := range infos {
ko, err := Bech32KeyOutput(info)
if err != nil {
return nil, err
}
kos[i] = ko
}
return kos, nil
}
// Bech32ConsKeyOutput create a KeyOutput in with "cons" Bech32 prefixes.
func Bech32ConsKeyOutput(keyInfo Info) (KeyOutput, error) {
consAddr := sdk.ConsAddress(keyInfo.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyConsPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: keyInfo.GetName(),
Type: keyInfo.GetType().String(),
Address: consAddr.String(),
PubKey: bechPubKey,
}, nil
}
// Bech32ValKeyOutput create a KeyOutput in with "val" Bech32 prefixes.
func Bech32ValKeyOutput(keyInfo Info) (KeyOutput, error) {
valAddr := sdk.ValAddress(keyInfo.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyValPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: keyInfo.GetName(),
Type: keyInfo.GetType().String(),
Address: valAddr.String(),
PubKey: bechPubKey,
}, nil
}
// Bech32KeyOutput create a KeyOutput in with "acc" Bech32 prefixes. If the
// public key is a multisig public key, then the threshold and constituent
// public keys will be added.
func Bech32KeyOutput(info Info) (KeyOutput, error) {
accAddr := sdk.AccAddress(info.GetPubKey().Address().Bytes())
bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
ko := KeyOutput{
Name: info.GetName(),
Type: info.GetType().String(),
Address: accAddr.String(),
PubKey: bechPubKey,
}
if mInfo, ok := info.(multiInfo); ok {
pubKeys := make([]multisigPubKeyOutput, len(mInfo.PubKeys))
for i, pk := range mInfo.PubKeys {
accAddr := sdk.AccAddress(pk.PubKey.Address().Bytes())
bechPubKey, err := sdk.Bech32ifyAccPub(pk.PubKey)
if err != nil {
return KeyOutput{}, err
}
pubKeys[i] = multisigPubKeyOutput{accAddr.String(), bechPubKey, pk.Weight}
}
ko.Threshold = mInfo.Threshold
ko.PubKeys = pubKeys
}
return ko, nil
}

View File

@ -1,7 +1,10 @@
package keys
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
@ -37,6 +40,9 @@ type Keybase interface {
// CreateOffline creates, stores, and returns a new offline key reference
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
// CreateMulti creates, stores, and returns a new multsig (offline) key reference
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
// The following operations will *only* work on locally-stored keys
Update(name, oldpass string, getNewpass func() (string, error)) error
Import(name string, armor string) (err error)
@ -59,12 +65,14 @@ const (
TypeLocal KeyType = 0
TypeLedger KeyType = 1
TypeOffline KeyType = 2
TypeMulti KeyType = 3
)
var keyTypes = map[KeyType]string{
TypeLocal: "local",
TypeLedger: "ledger",
TypeOffline: "offline",
TypeMulti: "multi",
}
// String implements the stringer interface for KeyType.
@ -82,11 +90,16 @@ type Info interface {
GetPubKey() crypto.PubKey
// Address
GetAddress() types.AccAddress
// Bip44 Path
GetPath() (*hd.BIP44Params, error)
}
var _ Info = &localInfo{}
var _ Info = &ledgerInfo{}
var _ Info = &offlineInfo{}
var (
_ Info = &localInfo{}
_ Info = &ledgerInfo{}
_ Info = &offlineInfo{}
_ Info = &multiInfo{}
)
// localInfo is the public information about a locally stored key
type localInfo struct {
@ -119,6 +132,10 @@ func (i localInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i localInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// ledgerInfo is the public information about a Ledger key
type ledgerInfo struct {
Name string `json:"name"`
@ -150,8 +167,9 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i ledgerInfo) GetPath() hd.BIP44Params {
return i.Path
func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
tmp := i.Path
return &tmp, nil
}
// offlineInfo is the public information about an offline key
@ -183,6 +201,58 @@ func (i offlineInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i offlineInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
type multisigPubKeyInfo struct {
PubKey crypto.PubKey `json:"pubkey"`
Weight uint `json:"weight"`
}
type multiInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
Threshold uint `json:"threshold"`
PubKeys []multisigPubKeyInfo `json:"pubkeys"`
}
func NewMultiInfo(name string, pub crypto.PubKey) Info {
multiPK := pub.(multisig.PubKeyMultisigThreshold)
pubKeys := make([]multisigPubKeyInfo, len(multiPK.PubKeys))
for i, pk := range multiPK.PubKeys {
// TODO: Recursively check pk for total weight?
pubKeys[i] = multisigPubKeyInfo{pk, 1}
}
return &multiInfo{
Name: name,
PubKey: pub,
Threshold: multiPK.K,
PubKeys: pubKeys,
}
}
func (i multiInfo) GetType() KeyType {
return TypeMulti
}
func (i multiInfo) GetName() string {
return i.Name
}
func (i multiInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
func (i multiInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i multiInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// encoding info
func writeInfo(i Info) []byte {
return cdc.MustMarshalBinaryLengthPrefixed(i)

View File

@ -4,10 +4,11 @@ import (
"encoding/hex"
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
)
func Test_writeReadLedgerInfo(t *testing.T) {
@ -20,7 +21,10 @@ func Test_writeReadLedgerInfo(t *testing.T) {
tmpKey,
*hd.NewFundraiserParams(5, 1)}
assert.Equal(t, TypeLedger, lInfo.GetType())
assert.Equal(t, "44'/118'/5'/0/1", lInfo.GetPath().String())
path, err := lInfo.GetPath()
assert.NoError(t, err)
assert.Equal(t, "44'/118'/5'/0/1", path.String())
assert.Equal(t,
"cosmospub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5wmxv8p",
types.MustBech32ifyAccPub(lInfo.GetPubKey()))
@ -36,5 +40,8 @@ func Test_writeReadLedgerInfo(t *testing.T) {
assert.Equal(t, lInfo.GetType(), restoredInfo.GetType())
assert.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey())
assert.Equal(t, lInfo.GetPath(), restoredInfo.(ledgerInfo).GetPath())
restoredPath, err := restoredInfo.GetPath()
assert.NoError(t, err)
assert.Equal(t, path, restoredPath)
}

View File

@ -3,10 +3,12 @@
package crypto
import (
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/tests"
"github.com/cosmos/go-bip39"
bip39 "github.com/cosmos/go-bip39"
"github.com/pkg/errors"
secp256k1 "github.com/tendermint/btcd/btcec"
"github.com/tendermint/tendermint/crypto"
@ -77,3 +79,9 @@ func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message [
sig2 := btcec.Signature{R: sig.R, S: sig.S}
return sig2.Serialize(), nil
}
// ShowAddressSECP256K1 shows the address for the corresponding bip32 derivation path
func (mock LedgerSECP256K1Mock) ShowAddressSECP256K1(bip32Path []uint32, hrp string) error {
fmt.Printf("Request to show address for %v at %v", hrp, bip32Path)
return nil
}

View File

@ -2,7 +2,7 @@
package crypto
import ledger "github.com/zondax/ledger-cosmos-go"
import ledger "github.com/cosmos/ledger-cosmos-go"
// If ledger support (build tag) has been enabled, which implies a CGO dependency,
// set the discoverLedger function which is responsible for loading the Ledger

View File

@ -5,12 +5,14 @@ import (
"os"
"github.com/btcsuite/btcd/btcec"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
tmbtcec "github.com/tendermint/btcd/btcec"
tmcrypto "github.com/tendermint/tendermint/crypto"
tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
)
var (
@ -31,6 +33,7 @@ type (
Close() error
GetPublicKeySECP256K1([]uint32) ([]byte, error)
SignSECP256K1([]uint32, []byte) ([]byte, error)
ShowAddressSECP256K1([]uint32, string) error
}
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano we
@ -61,6 +64,26 @@ func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params) (tmcrypto.PrivKey, error) {
return PrivKeyLedgerSecp256k1{pubKey, path}, nil
}
// LedgerShowAddress triggers a ledger device to show the corresponding address.
func LedgerShowAddress(path hd.BIP44Params, expectedPubKey tmcrypto.PubKey) error {
device, err := getLedgerDevice()
if err != nil {
return err
}
defer warnIfErrors(device.Close)
pubKey, err := getPubKey(device, path)
if err != nil {
return err
}
if pubKey != expectedPubKey {
return fmt.Errorf("pubkey does not match, Check this is the same device")
}
return device.ShowAddressSECP256K1(path.DerivationPath(), types.Bech32PrefixAccAddr)
}
// PubKey returns the cached public key.
func (pkl PrivKeyLedgerSecp256k1) PubKey() tmcrypto.PubKey {
return pkl.CachedPubKey

View File

@ -151,7 +151,7 @@ func handleFrom(store sdk.KVStore, from sdk.AccAddress, amt sdk.Coins) sdk.Resul
}
// Deduct msg amount from sender account.
senderCoins := acc.Coins.Minus(amt)
senderCoins := acc.Coins.Sub(amt)
// If any coin has negative amount, return insufficient coins error.
if senderCoins.IsAnyNegative() {
@ -188,7 +188,7 @@ func handleTo(store sdk.KVStore, to sdk.AccAddress, amt sdk.Coins) sdk.Result {
}
// Add amount to receiver's old coins
receiverCoins := acc.Coins.Plus(amt)
receiverCoins := acc.Coins.Add(amt)
// Update receiver account
acc.Coins = receiverCoins

View File

@ -457,9 +457,9 @@ If you do not have a ledger device and want to interact with your private key on
```bash
// Bond Atoms
// ex value for flags: <amountToBound>=10000stake, <bech32AddressOfValidator>=cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, <gasPrice>=0.001stake
// ex value for flags: <amountToBound>=10000stake, <bech32AddressOfValidator>=cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, <gasPrice>=0.001stake, <delegatorAddress>=cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg
gaiacli tx staking delegate <validatorAddress> <amountToBond> --from <delegatorKeyName> --gas auto --gas-prices <gasPrice> --generate-only > unsignedTX.json
gaiacli tx staking delegate <validatorAddress> <amountToBond> --from <delegatorAddress> --gas auto --gas-prices <gasPrice> --generate-only > unsignedTX.json
```
Then, copy `unsignedTx.json` and transfer it (e.g. via USB) to the offline computer. If it is not done already, [create an account on the offline computer](#using-a-computer). For additional security, you can double check the parameters of your transaction before signing it using the following command:

View File

@ -10,6 +10,16 @@ Supporting code can be found in the [networks directory](https://github.com/cosm
> NOTE: The `remote` network bootstrapping may be out of sync with the latest releases and is not to be relied upon.
## Available Docker images
In case you need to use or deploy gaia as a container you could skip the `build` steps and use the official images, $TAG stands for the version you are interested in:
- `docker run -it -v ~/.gaiad:/root/.gaiad -v ~/.gaiacli:/root/.gaiacli tendermint:$TAG gaiad init`
- `docker run -it -p 26657:26657 -p 26656:26656 -v ~/.gaiad:/root/.gaiad -v ~/.gaiacli:/root/.gaiacli tendermint:$TAG gaiad start`
- ...
- `docker run -it -v ~/.gaiad:/root/.gaiad -v ~/.gaiacli:/root/.gaiacli tendermint:$TAG gaiacli version`
The same images can be used to build your own docker-compose stack.
## Single-node, local, manual testnet
This guide helps you create a single validator node that runs a network locally for testing and other development related uses.
@ -34,7 +44,7 @@ gaiacli keys add validator
# Add that key into the genesis.app_state.accounts array in the genesis file
# NOTE: this command lets you set the number of coins. Make sure this account has some coins
# with the genesis.app_state.staking.params.bond_denom denom, the default is staking
gaiad add-genesis-account $(gaiacli keys show validator -a) 1000stake,1000validatortoken
gaiad add-genesis-account $(gaiacli keys show validator -a) 1000000000stake,1000000000validatortoken
# Generate the transaction that creates your validator
gaiad gentx --name validator

View File

@ -683,7 +683,7 @@ gaiacli query distr rewards <delegator_address>
Multisig transactions require signatures of multiple private keys. Thus, generating and signing
a transaction from a multisig account involve cooperation among the parties involved. A multisig
transaction can be initiated by any of the key holders, and at least one of them would need to
import other parties' public keys into their local database and generate a multisig public key
import other parties' public keys into their Keybase and generate a multisig public key
in order to finalize and broadcast the transaction.
For example, given a multisig key comprising the keys `p1`, `p2`, and `p3`, each of which is held
@ -692,17 +692,17 @@ generate the multisig account public key:
```
gaiacli keys add \
--pubkey=cosmospub1addwnpepqtd28uwa0yxtwal5223qqr5aqf5y57tc7kk7z8qd4zplrdlk5ez5kdnlrj4 \
p2
p2 \
--pubkey=cosmospub1addwnpepqtd28uwa0yxtwal5223qqr5aqf5y57tc7kk7z8qd4zplrdlk5ez5kdnlrj4
gaiacli keys add \
--pubkey=cosmospub1addwnpepqgj04jpm9wrdml5qnss9kjxkmxzywuklnkj0g3a3f8l5wx9z4ennz84ym5t \
p3
p3 \
--pubkey=cosmospub1addwnpepqgj04jpm9wrdml5qnss9kjxkmxzywuklnkj0g3a3f8l5wx9z4ennz84ym5t
gaiacli keys add \
--multisig-threshold=2
p1p2p3 \
--multisig-threshold=2 \
--multisig=p1,p2,p3
p1p2p3
```
A new multisig public key `p1p2p3` has been stored, and its address will be
@ -712,6 +712,15 @@ used as signer of multisig transactions:
gaiacli keys show --address p1p2p3
```
You may also view multisig threshold, pubkey constituents and respective weights
by viewing the JSON output of the key or passing the `--show-multisig` flag:
```bash
gaiacli keys show p1p2p3 -o json
gaiacli keys show p1p2p3 --show-multisig
```
The first step to create a multisig transaction is to initiate it on behalf
of the multisig address created above:
@ -726,10 +735,10 @@ The file `unsignedTx.json` contains the unsigned transaction encoded in JSON.
```bash
gaiacli tx sign \
unsignedTx.json \
--multisig=<multisig_address> \
--name=p1 \
--from=p1 \
--output-document=p1signature.json \
unsignedTx.json
```
Once the signature is generated, `p1` transmits both `unsignedTx.json` and
@ -738,10 +747,10 @@ respective signature:
```bash
gaiacli tx sign \
unsignedTx.json \
--multisig=<multisig_address> \
--name=p2 \
--from=p2 \
--output-document=p2signature.json \
unsignedTx.json
```
`p1p2p3` is a 2-of-3 multisig key, therefore one additional signature

View File

@ -0,0 +1,116 @@
# **Cosmos主网上线三部曲**
Cosmos主网启动将分成3个阶段分布完成下面我们将具体介绍每一阶段的预期目标。
## **🚨第一阶段:网络逐步趋于稳定🚨**
在第一阶段主网可能会不太稳定也许会出现暂停或其他故障可能需要Cosmos主网验证人和全节点运营者们一起来协助修复。在网络趋于稳定过程中出现此类故障并不意外。
**状态修改和主网启动:**
区块链的核心价值之一是其不可篡改性,也就是说我们不会通过回滚来修改过去的状态记录。最终,这种不可篡改的理念在软件共识协议层面得到了支持,并最终在社区参与者之间形成了社会契约。
也就是说Cosmos Hub的底层技术开发是能够实现低难度的分叉和回滚的 我们已经看到社区在测试网上做过多次相应的演练。这些技术也会在主网上应用,用以最终抵御卡特尔风险的发生。
回滚网络状态通常被认为是非常严重的问题因为这将损害网络的经济最终性。因此回滚网络状态只能在极端状态下使用比如以太网的DAO硬分叉也就是说在Cosmos Hub主网启动初期转账交易不会开启因此回滚的危害性很小因为状态转换远比“经济最终性”的影响低。 如果需要,比如发现漏洞,我们可以将过去某一个高度时的网络状态导出,然后重启网络,就如同我们在测试网上演练过的那样。
一旦链上治理决定开启交易功能,全网将会遵从经济最终性。
总而言之如果在链上交易功能开启之前Cosmos Hub发现任何错误或漏洞那么用户可期望回滚至任意状态甚至创世块。
一旦链上交易功能开始后,状态回滚方式将很难被采纳。
**对开发人员的建议**Cosmos主网启动是投资者参与主网的第一阶段。作为分布式应用的开发人员您可能是Cosmos-SDK框架或Tendermint Core的用户。开发者基于[Cosmos-SDK](https://cosmos.network/docs/)或[Tendermint](https://tendermint.com/docs/)的应用开发进度现阶段应该不受Cosmos Hub的影响但如果您的项目需要使用[IBC](https://blog.cosmos.network/developer-deep-dive-cosmos-ibc-5855aaf183fe)链间通信协议则必须要等到第三阶段或参与即将开始的IBC测试网络。
**对于用户的建议**在此阶段我们强烈建议您不要交易Atoms其实法律合同也限定不能交易因为在这个阶段仍然存在状态回滚的风险。
但是,您可以通过下面链接的[CLI指南](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/gaia/delegator-guide-cli.md)和视频教程的指导在此阶段将Atoms通证委托给验证人。当然如果发生状态修改那么所有收益包括手续费和通胀收益都将丢失。Voyager是用于与Cosmos Hub交互的图形化客户端目前处于alpha版正在持续开发中。一旦Voager beta版上线并可以安全使用我们将另行公告。
CLI指南和视频教程https://v.qq.com/x/page/q08425izfhi.html
## 第二阶段:链上交易开启
**摘要:**
一旦我们认为主网足够稳定在链上抵押了Atom的通证持有者将会通过链上治理过程投票决定是否开启链上交易。
Cosmos浏览器是查看治理提案状态的最佳途径可以在我们的主网启动页面上找到多款[Cosmos浏览器](https://cosmos.network/launch)。
对用户来说如果提案被社区接受并且链上交易开启您就可以在链上转移您的Atom了。
## 第三阶段启用IBC协议
**摘要:**
第三阶段我们将会发布[IBC协议](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/spec/ibc/overview.md)并且对是否将其纳入Cosmos-SDK的核心模块库进行链上治理投票。
**对开发人员的建议**使用Cosmos-SDK或Tendermint BFT构建的应用专有链将能够连接到 Cosmos Hub并与连接到Hubs上的其他任意区块链进行跨链交互。
**对用户的建议**您可以将各种通证和NFT直接从采用IBC协议连接的一个链转移到另一个通过IBC协议连接的链而无需通过一个第三方中心化平台。
## 验证人提示: 提交您的主网gentx文件
1. 验证人应该已经生成并且安全的保存好用于主网验证的共识密钥。
2. 验证人根据自己获得ATOM通证的来源比如募资参与者或权益争夺活动的获奖者准备好在创世文件Genesis中需要签署的交易。
3. 一旦创世通证分配计划发布我们将开始征集gentx文件。
## 总结
Cosmos旨在建立所有区块链间的桥梁最终建立一个区块链互联网。然而路漫漫其修远兮我们还有很长的路要走。主网上线之后我们需要完成与整个通证经济世界的深度融合很多的工作要做等着我们去完成。正如肯尼迪在逆境中曾经说过的那样
“我们选择去月球,不是因为很容易,而是因为很难......”
走更崎岖的路,才会看见更美的风景!
****

View File

@ -0,0 +1,67 @@
# The 3 Phases of the Cosmos Hub Mainnet
## Post-Mainnet Development Roadmap & Expectations for Users
The launch of the Cosmos Hub mainnet is expected to happen in phases. Here we outline what to expect in each phase.
# 🚨Phase I: Network Gains Stability 🚨
In the first phase, the network is likely to be unstable; it may experience halts or other forms of failure requiring intervention and coordination among Cosmos Hub validators and full node operators to deploy a fix. This type of failure is not unexpected while the network gains stability.
## State Reversions and Mainnet launch
One of the core ideologies around blockchains is immutability. This is the idea that we don't go
back and edit past state transitions. While this notion of immutability is implemented directly via consensus protocols in the software, it is ultimately upheld by social contract among participants.
That said, the technology underlying the Cosmos Hub was intentionally developed to enable low-friction forks and rollbacks. Weve seen the community practice these techniques numerous times on the test networks. Its likely they will need to be used on a mainnet as well. Ultimately, they are a countervailing force to the risk of cartel takeover.
Reverting state is often seen as highly grievous, as it compromises the networks economic finality. Hence it should only be used in extreme conditions, as witnessed in the case of Ethereum with the DAO Hard Fork. That said, in the early days of the Cosmos Hub network, transfers will not be active, and hence the severity of state reversions will be reduced, as state transitions will be much less “economically final”. If necessary in case of bugs, the state can be exported from a past height and the network restarted, as practiced on the testnets.
Once governance chooses to enable transfers, the importance of economic finality must be respected by the network.
To summarize, if there are errors or vulnerabilities in the Cosmos Hub in the days before transfers are enabled, users should expect arbitrary state rollbacks even to genesis.
Once transfers are enabled, state rollbacks will be much more difficult to justify.
**What this means for developers:** The Cosmos mainnet launch is the first phase in which fundraiser participants will be working together to operate the software. As a decentralized application developer, you are likely a user of either the [Cosmos-SDK framework](https://cosmos.network/docs/) or [Tendermint Core](https://tendermint.com/docs/). The progress of your Cosmos-SDK or Tendermint-based application should be independent of the Cosmos Hub roadmap. However, if your project requires the use of [Inter-Blockchain Communication][blog post], you must wait until Phase III, or participate in the IBC testnets that will begin shortly.
**What this means for users:** In this phase, we strongly recommend that you do not arrange to trade Atoms (eg. by legal contract as they will not be transferable yet) as there is the risk of state being reverted.
You can, however, safely delegate Atoms to validators in this phase by following the CLI guideline and video tutorial linked below. Of course, in the event of a state reversion, any earned fees and inflation may be lost. Note that only `gaiacli` should be used for making transactions. Voyager, the GUI for interacting with the Cosmos Hub, is currently in alpha and undergoing development. A separate announcement will be made once Voyager is safer for use.
CLI Guide 🔗: [github.com/cosmos/cosmos-sdk/…/delegator-guide-cli.md](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/gaia/delegator-guide-cli.md)
**Watch CLI delegation tutorial:** [Cosmos YouTube](https://www.youtube.com/watch?v=ydZw6o6Mzy0)
# Phase II: Transfers Enabled
**Summary:** Once mainnet is deemed sufficiently stable, bonded Atom holders will vote to decide whether or not Atom transfers should be enabled. This procedure will happen through on-chain governance.
The best way to check on the status of governance proposals is to view them through Cosmos explorers. A list of explorers can be found on the launch page: [cosmos.network/launch](https://cosmos.network/launch).
**What this means for users:** If the proposal is accepted and transfers are enabled, then it becomes possible to transfer Atoms.
# Phase III: IBC Enabled
**Summary:** In Phase III, the [IBC protocol][ibc] is released and Atom holders vote via on-chain governance on whether or not to enable it as part of the core module library within the Cosmos-SDK.
**What this means for developers:** Application-specific blockchains that are built using the Cosmos-SDK or Tendermint BFT will be able to connect to the Hub and interoperate/compose with all of the other blockchains that are connected to it.
**What this means for users:** You will be able to transfer various tokens and NFTs directly from one IBC-connected chain to another IBC-connected chain without going through a centralized
third-party platform.
# Housekeeping for Validators: Submitting a `gentx` for mainnet
1. You should have generated and secured the validator consensus key you are going to be validating under during mainnet.
2. Be prepared to sign a transaction under an address in the genesis file either from the fundraiser or Game of Stakes depending on where you received your ATOM allocation.
3. We will begin collecting Gentxs for mainnet once the recommended genesis allocations are published.
# In Closing
The Cosmos mission is to build bridges to connect all blockchains—to build an Internet of Blockchains. Clearly, we have a long road of development ahead of us. And after mainnet, the real work to a world of deeply integrated token economies is still ahead of us. But as John Fitzgerald Kennedy once said in the face of adversity:
*“We choose to go to the moon...not because they are easy, but because they are hard….”*
To the Moon 🚀
[blog post]: [https://blog.cosmos.network/developer-deep-dive-cosmos-ibc-5855aaf183fe]
[ibc]: [https://github.com/cosmos/cosmos-sdk/blob/develop/docs/spec/ibc/overview.md]

View File

@ -0,0 +1,74 @@
# 코스모스 허브 메인넷의 세 가지 단계
## 메인넷 후 개발 로드맵과 유저들을 위한 필수 정보
코스모스 허브의 런칭은 단계별로 나뉘어 진행될 계획입니다. 다음은 각 단계별로 어떤 사항들이 진행될지에 대한 요약입니다.
# 🚨1단계: 네트워크 안정성 확보 🚨
메인넷의 최초 단계인 1단계에서 코스모스 네트워크는 다소 불안정할 수 있습니다. 메인넷 초기에는 네트워크 중단 같은 문제들이 발생할 확률이 존재합니다.
혹시라도 발생할 수 있는 문제들을 해결하기 위해서는 코스모스 허브 검증인 간, 풀노드 운영자 등 참여자들 간의 긴밀한 협업이 필요합니다.
유저는 충분한 네트워크 안정성이 확보될 때까지 문제들이 발생할 수 있다는 것을 인지해야 합니다.
**블록체인 롤백과 메인넷 런칭**
블록체인 기술의 핵심 이념 중 하나는 불가역성(immutability)입니다.
불가역성이란 이미 진행된 블록체인의 기록을 되돌아가 바꾸지 않는다는 것입니다. 블록체인의 불가역성은 소프트웨어 계층에서 합의 프로토콜의 형태로 전개되지만, 궁극적으로 네트워크 참여자들의 사회적 계약(social contract) 형태로 유지됩니다.
여기서 참고할 것은 코스모스 허브는 의도적으로 포크와 롤백 절차를 간소화할 수 있도록 설계되었다는 것입니다. 코스모스 커뮤니티는 테스트넷 단계에서 이런 기능을 실제 사용했으며, 메인넷에서 또한 사용될 수 있습니다. 포크와 롤백 기능은 특정 카르텔 집단이 네트워크를 점령하는 것에 대한 대응 도구로 사용될 수 있습니다.
대다수의 블록체인 커뮤니티는 롤백을 부정적으로 인식하고 있으며 매우 신중하게 결정되어야 합니다. 블록체인의 상태를 되돌린다는 것은 블록체인 네트워크의 경제적 완결성(economic finality)을 깨트리는 행동입니다. 그렇기 때문에 블록체인 롤백은 이더리움의 The DAO 하드포크 같은 극한 상황 상황에서만 사용되어야 합니다.
단, 코스모스 허브 네트워크의 초기 단계에서는 토큰 전송 기능이 비활성화된 상태로 시작됩니다. 이런 특성은 블록체인 롤백의 중대성을 줄이게 되며 블록체인 상태의 변경의 ‘경제적 완결성’을 낮추게 됩니다. 버그 등 문제 발생으로 블록체인 롤백이 필요하다는 판단이 내려지는 경우, 테스트넷과 같이 블록체인 상태 롤백이 제네시스 블록까지도 진행될 수 있습니다.
거버넌스에 의하여 토큰 전송이 활성화되는 경우, 코스모스 네트워크는 경제적 완결성을 따르게 됩니다.
위 내용을 요약하자면, 토큰 전송이 활성화되기 전 코스모스 허브에 에러 또는 취약점이 발견되는 경우, 블록체인의 (제네시스 상태까지) 롤백이 진행될 수 있습니다.
하지만 토큰 전송이 활성화된 뒤라면 블록체인 롤백을 정당화하는 것은 상당히 어렵게 됩니다.
**개발자 참고 사항**: 코스모스 메인넷 런칭은 펀드레이저 참가자들이 협동하여 코스모스 허브 소프트웨어를 운영하는 첫 단계입니다. 생태계 내 대다수의 탈중앙화 애플리케이션 개발자는 [코스모스 SDK](https://cosmos.network/docs/) 또는 [텐더민트 코어](https://tendermint.com/docs/)를 사용하고 있는 것으로 예상됩니다. 각 코스모스 SDK/텐더민트 기반 애플리케이션의 개발 진행은 코스모스 허브와 별도로 진행되어도 무관합니다. 다만, [IBC](https://blog.cosmos.network/developer-deep-dive-cosmos-ibc-5855aaf183fe)(Inter-Blockchain Communication)을 사용하기 위해서는 메인넷 3단계까지 기다리거나 IBC 테스트넷에서 시범운영을 하실 수 있습니다.
**유저 참고 사항**: 메인넷 최초 단계에서는 코스모스 아톰을 거래하지 **않는** 것을 강력하게 권고합니다 (예, 법적 계약을 통한 거래). 1단계에서는 제네시스 블록까지 긴급 롤백이 진행될 수 있으므로 트랜잭션이 번복될 수 있는 위험이 존재합니다.
다만, 유저는 CLI 가이드 또는 하단의 영상 투토리얼을 참고하여 안전하게 아톰 위임을 진행하실 수는 있습니다. 다만 만약 블록체인 롤백이 진행되는 경우, 스테이킹으로 받은 리워드와 인플레이션 리워드의 손실이 있습니다.
첫 단계에서 오직 CLI를 사용해서 트랜잭션을 발생하는 것을 코스모스 허브와 소통할 때 사용될 보이저(Voyager) 지갑은 현재 알파 단계에 있기 때문에 안전하게 사용할 수 있는 프로그램은 CLI 뿐이라는 것을 강조합니다. 보이저 지갑이 안전하다는 판단이 내려지고 베타가 시작될때 별도의 공지가 있겠습니다.
CLI 가이드 🔗: [https://github.com/cosmos/cosmos-sdk/blob/develop/docs/translations/kr/gaia/delegator-guide-cli.md](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/translations/kr/gaia/delegator-guide-cli.md)
CLI 위임 투토리얼 (영어): [https://www.youtube.com/watch?v=ydZw6o6Mzy0](https://www.youtube.com/watch?v=ydZw6o6Mzy0)
# 2단계: 토큰 전송 활성화
**요약**: 메인넷이 충분히 안정성을 확보했다는 판단이 내려진 후, 위임된 아톰을 보유한 홀더들은 아톰 토큰 전송 활성화에 대한 프로포절에 투표를 할 수 있습니다. 해당 절차는 블록체인 상의 거버넌스 절차를 통해 진행됩니다.
거버넌스 프로포절 현황에 대한 정보는 다양한 코스모스 익스플로러를 통해 확인하실 수 있습니다. 코스모스 익스플로러 목록은 [여기](https://cosmos.network/launch)를 참고하세요.
**유저 참고 사항**: 토큰 전송 활성화 프로포절이 통과하고 전송이 활성화된 후 아톰 이동이 가능해집니다.
# 3단계: IBC 활성화
**요약**: 3단계에서는 IBC 프로토콜이 공개됩니다. 토큰 홀더는 IBC 기능을 코스모스 SDK의 코어 모듈 라이브러리로 활성화할지에 대한 거버넌스 투표를 하게됩니다.
**개발자 참고 사항**: 이 단계에서 코스모스 SDK/텐더민트 BFT를 기반으로 만들어진 애플리케이션 특화 블록체인(application-specific blockchain)은 IBC를 통해 중앙화된 제삼자 없이 코스모스 허브와 소통할 수 있게 됩니다. IBC를 이용해 블록체인 간 다양한 토큰과 NFT(Non-fungible token)들의 이동이 가능해집니다.
# 메인넷 검증인 준비 사항
1. 메인넷에서 사용할 검증인 컨센서스 키(validator consensus key)를 생성하고 안전한 형태로 보관하십시오.
2. 게임 오브 스테이크 상금 / 펀드레이저 계정에서 트랜잭션 생성 및 서명을 할 수 있도록 준비하십시오
3. ICF의 제네시스 토큰 분배 제안이 공개된 후 메인넷 Gentx 수집 작업을 시작합니다.
# 글을 마무리 하며
코스모스의 목적은 모든 블록체인을 연결하여 ‘블록체인의 인터넷’을 만드는 것입니다.
앞으로 가야 할 길이 멀다는 것을 알고 있습니다.
메인넷이 런칭된 후, 토큰 이코노미 기반의 세상을 설계하는 본격적인 일이 본격적으로 시작돼야 할 것입니다.
하지만 미국 전 대통령 존 케네디의 말을 인용하자면, “우리는 달에 가기로 결정하였습니다. 그것이 쉽기 때문이 아니라 어렵기 때문에 이렇게 결정한 것입니다.”
달에 도착하는 그 날까지… 🚀

View File

@ -8,29 +8,29 @@ This is work in progress. Mechanisms and values are susceptible to change.
### What is a validator?
The [Cosmos Hub](/introduction/cosmos-hub.md) is based on [Tendermint](/introduction/tendermint.md), which relies on a set of [validators](/validators/overview.md) to secure the network. The role of validators is to run a full-node and participate in consensus by broadcasting votes which contain cryptographic signatures signed by their private key. Validators commit new blocks in the blockchain and receive revenue in exchange for their work. They must also participate in governance by voting on proposals. Validators are weighted according to their total stake.
The [Cosmos Hub](../what-is-gaia.md) is based on [Tendermint](https://tendermint.com/docs/introduction/what-is-tendermint.html), which relies on a set of validators to secure the network. The role of validators is to run a full-node and participate in consensus by broadcasting votes which contain cryptographic signatures signed by their private key. Validators commit new blocks in the blockchain and receive revenue in exchange for their work. They must also participate in governance by voting on proposals. Validators are weighted according to their total stake.
### What is 'staking'?
The Cosmos Hub is a public Proof-Of-Stake (PoS) blockchain, meaning that validator's weight is determined by the amount of staking tokens (Atoms) bonded as collateral. These Atoms can be staked directly by the validator or delegated to them by Atom holders.
The Cosmos Hub is a public Proof-Of-Stake (PoS) blockchain, meaning that the weight of validators is determined by the amount of staking tokens (Atoms) bonded as collateral. These Atoms can be seld-delegated directly by the validator or delegated to them by other Atom holders.
Any user in the system can declare its intention to become a validator by sending a `create-validator` transaction. From there, they become validators.
Any user in the system can declare their intention to become a validator by sending a `create-validator` transaction. From there, they become validator candidates.
The weight (i.e. total stake) of a validator determines wether or not it is an active validator, and also how frequently this node will have to propose a block and how much revenue it will obtain. Initially, only the top 100 validators with the most weight will be active validators. If validators double sign, are frequently offline or do not participate in governance, their staked Atoms (including Atoms of users that delegated to them) can be destroyed, or 'slashed'.
The weight (i.e. voting power) of a validator determines wether or not they are an active validator. Initially, only the top 100 validators with the most voting power will be active validators.
### What is a full-node?
A full-node is a program that fully validates transactions and blocks of a blockchain. It is distinct from a light-node that only processes block headers and a small subset of transactions. Running a full-node requires more resources than a light-node but is necessary in order to be a validator. In practice, running a full-node only implies running a non-compromised and up-to-date version of the software with low network latency and without downtime.
Of course, it is possible and encouraged for any user to run full-nodes even if they do not plan to be validators.
Of course, it is possible and encouraged for users to run full-nodes even if they do not plan to be validators.
### What is a delegator?
Delegators are Atom holders who cannot, or do not want to run validator operations themselves. Through [Cosmos Voyager](/getting-started/voyager.md), a user can delegate Atoms to a validator and obtain a part of its revenue in exchange (for more detail on how revenue is distributed, see **What is the incentive to stake?** and **What is a validator's commission?** sections below).
Delegators are Atom holders who cannot, or do not want to run a validator themselves. Atom holders can delegate Atoms to a validator and obtain a part of their revenue in exchange (for more detail on how revenue is distributed, see [**What is the incentive to stake?**](#what-is-the-incentive-to-stake?) and [**What are validators commission?**](#what-are-validators-commission?) sections below).
Because they share revenue with their validators, delegators also share responsibility. Should a validator misbehave, each of its delegators will be partially slashed in proportion to their stake. This is why delegators should perform due diligence on validators before delegating, as well as spreading their stake over multiple validators.
Because they share revenue with their validators, delegators also share risks. Should a validator misbehave, each of their delegators will be partially slashed in proportion to their delegated stake. This is why delegators should perform due diligence on validators before delegating, as well as spreading their stake over multiple validators.
Delegators play a critical role in the system, as they are responsible for choosing validators. Being a delegator is not a passive role: Delegators should actively monitor the actions of their validators and participate in governance.
Delegators play a critical role in the system, as they are responsible for choosing validators. Being a delegator is not a passive role: Delegators should actively monitor the actions of their validators and participate in governance. For more, read the [delegator's faq](https://cosmos.network/resources/delegators).
## Becoming a Validator
@ -38,20 +38,19 @@ Delegators play a critical role in the system, as they are responsible for choos
Any participant in the network can signal that they want to become a validator by sending a `create-validator` transaction, where they must fill out the following parameters:
* Validator's PubKey: The private key associated with PubKey is used to sign _prevotes_ and _precommits_. This way, validators can have different accounts for validating and holding liquid funds.
* Validator's Address: Application level address. This is the address used to identify your validator publicly. The private key associated with this address is used to bond, unbond, claim rewards, and participate in governance (in MVP only).
* Validator's name (moniker)
* Validator's website (Optional)
* Validator's description (Optional)
* Initial commission rate: The commission rate on block provisions, block rewards and fees charged to delegators
* Maximum commission: The maximum commission rate which this validator can charge
* Commission change rate: The maximum daily increase of the validator commission
* Minimum self-bond amount: Minimum amount of Atoms the validator need to have bonded at all time. If the validator's self-bonded stake falls below this limit, its entire staking pool will unbond.
* Initial self-bond amount: Initial amount of Atoms the validator wants to self-bond
* **Validator's `PubKey`:** The private key associated with this Tendermint `PubKey` is used to sign _prevotes_ and _precommits_.
* **Validator's Address:** Application level address. This is the address used to identify your validator publicly. The private key associated with this address is used to delegate, unbond, claim rewards, and participate in governance.
* **Validator's name (moniker)**
* **Validator's website (Optional)**
* **Validator's description (Optional)**
* **Initial commission rate**: The commission rate on block rewards and fees charged to delegators.
* **Maximum commission:** The maximum commission rate which this validator can charge. This parameter cannot be changed after `create-validator` is processed.
* **Commission max change rate:** The maximum daily increase of the validator commission. This parameter cannot be changed after `create-validator` is processed.
* **Minimum self-delegation:** Minimum amount of Atoms the validator needs to have bonded at all time. If the validator's self-delegated stake falls below this limit, their entire staking pool will unbond.
Once a validator is created, Atom holders can delegate atoms to it, effectively adding stake to this pool. The total stake of an address is the combination of Atoms bonded by delegators and Atoms self-bonded by the entity which designated itself.
Once a validator is created, Atom holders can delegate atoms to them, effectively adding stake to their pool. The total stake of an address is the combination of Atoms bonded by delegators and Atoms self-bonded by the entity which designated themselves.
Out of all validators that signaled themselves, the 100 with the most stake are the ones who are designated as validators. They become **bonded validators** If a validator's total stake falls below the top 100 then that validator loses its validator privileges, it enters **unbonding mode** and, eventually, becomes **unbonded** . Over time, the maximum number of validators will increase, according to a predefined schedule:
Out of all validator candidates that signaled themselves, the 100 with the most total stake are the ones who are designated as validators. They become **validators** If a validator's total stake falls below the top 100 then that validator loses their validator privileges: they don't participate in consensus and generate rewards any more. Over time, the maximum number of validators will increase, according to the following schedule (*Note: this schedule can be changed by governance*):
* **Year 0:** 100
* **Year 1:** 113
@ -71,95 +70,89 @@ Out of all validators that signaled themselves, the 100 with the most stake are
The Testnet is a great environment to test your validator setup before launch.
We view testnet participation as a great way to signal to the community that you are ready and able to operate a validator. You can find all relevant information about the testnet [here](https://github.com/cosmos/cosmos-sdk/tree/develop/cmd/gaia/testnets) and [here](https://github.com/cosmos/testnets).
We view testnet participation as a great way to signal to the community that you are ready and able to operate a validator. You can find all relevant information about the testnet [here](../join-testnet.md) and [here](https://github.com/cosmos/testnets).
### What are the different types of keys?
In short, there are two types of keys:
* **Tendermint Key**: This is a unique key used to sign block hashes. It is associated with a public key `cosmosvalconspub`.
* Generated when the node is created with gaiad init.
* Get this value with `gaiad tendermint show-validator`
e.g. `cosmosvalconspub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c`
- **Tendermint Key**: This is a unique key used to sign consensus votes.
+ It is associated with a public key `cosmosvalconspub` (Get this value with `gaiad tendermint show-validator`)
+ It is generated when the node is created with gaiad init.
- **Application key**: This key is created from `gaiacli` and used to sign transactions. Application keys are associated with a public key prefixed by `cosmospub` and an address prefixed by `cosmos`. Both are derived from account keys generated by `gaiacli keys add`.
* **Application keys**: These keys are created from the application and used to sign transactions. As a validator, you will probably use one key to sign staking-related transactions, and another key to sign governance-related transactions. Application keys are associated with a public key `cosmospub` and an address `cosmos`. Both are derived from account keys generated by `gaiacli keys add`.
* Note: A validator's operator key is directly tied to an application key, but
uses reserved prefixes solely for this purpose: `cosmosvaloper` and `cosmosvaloperpub`
Note: A validator's operator key is directly tied to an application key, but
uses reserved prefixes solely for this purpose: `cosmosvaloper` and `cosmosvaloperpub`
### What are the different states a validator can be in?
After a validator is created with a `create-validator` transaction, it can be in three states:
After a validator is created with a `create-validator` transaction, they can be in three states:
- `bonded`: Validator is in the active set and participates in consensus. Validator is earning rewards and can be slashed for misbehaviour.
- `unbonding`: Validator is not in the active set and does not participate in consensus. Validator is not earning rewards, but can still be slashed for misbehaviour. This is a transition state from `bonded` to `unbonded`. If validator does not send a `rebond` transaction while in `unbonding` mode, it will take three weeks for the state transition to complete.
- `in validator set`: Validator is in the active set and participates in consensus. Validator is earning rewards and can be slashed for misbehaviour.
- `jailed`: Validator misbehaved and is in jail, i.e. outisde of the validator set. If the jailing is due to being offline for too long, the validator can send an `unjail` transaction in order to re-enter the validator set. If the jailing is due to double signing, the validator cannot unjail.
- `unbonded`: Validator is not in the active set, and therefore not signing blocs. Validator cannot be slashed, and does not earn any reward. It is still possible to delegate Atoms to this validator. Un-delegating from an `unbonded` validator is immediate.
Delegators have the same state as their validator.
*Note that delegation are not necessarily bonded. Atoms can be delegated and bonded, delegated and unbonding, delegated and unbonded, or liquid*
### What is 'self-delegation'? How can I increase my 'self-delegatino'?
Self-delegation is delegation from a validator to themselves. This amount can be increases by sending a `delegate` transaction from your validator's `application` application key.
### What is 'self-bond'? How can I increase my 'self-bond'?
### Is there a minimum amount of Atoms that must be delegated to be an active (=bonded) validator?
### Is there a faucet?
If you want to obtain coins for the testnet, you can do so by using [this faucet](https://gaia.faucetcosmos.network/)
### Is there a minimum amount of Atoms that must be staked to be an active (=bonded) validator?
There is no minimum. The top 100 validators with the highest total stake (where total stake = self-bonded stake + delegators stake) are the active validators.
The minimum is `1 atom`.
### How will delegators choose their validators?
Delegators are free to choose validators according to their own subjective criteria. This said, criteria anticipated to be important include:
* **Amount of self-bonded Atoms:** Number of Atoms a validator self-bonded to its staking pool. A validator with higher amount of self-bonded Atoms has more skin in the game, making it more liable for its actions.
* **Amount of delegated Atoms:** Total number of Atoms delegated to a validator. A high stake shows that the community trusts this validator, but it also means that this validator is a bigger target for hackers. Indeed, hackers are incentivized to hack bigger validators as they receive a reward proportionate to the stake of the validator they can prove to have compromised. Validators are expected to become less and less attractive as their amount of delegated Atoms grows.
* **Commission rate:** Commission applied on revenue by validators before it is distributed to their delegators
* **Amount of self-delegated Atoms:** Number of Atoms a validator self-delegated to themselves. A validator with a higher amount of self-delegated Atoms has more skin in the game, making them more liable for their actions.
* **Amount of delegated Atoms:** Total number of Atoms delegated to a validator. A high voting power shows that the community trusts this validator, but it also means that this validator is a bigger target for hackers. Bigger validators also decrease the decentralisation of the network.
* **Commission rate:** Commission applied on revenue by validators before it is distributed to their delegators.
* **Track record:** Delegators will likely look at the track record of the validators they plan to delegate to. This includes seniority, past votes on proposals, historical average uptime and how often the node was compromised.
Apart from these criteria that will be displayed in Cosmos Voyager, there will be a possibility for validators to signal a website address to complete their resume. Validators will need to build reputation one way or another to attract delegators. For example, it would be a good practice for validators to have their setup audited by third parties. Note though, that the Tendermint team will not approve or conduct any audit itself. For more on due diligence, see [this blog post](https://medium.com/@interchain_io/3d0faf10ce6f)
Apart from these criteria, there will be a possibility for validators to signal a website address to complete their resume. Validators will need to build reputation one way or another to attract delegators. For example, it would be a good practice for validators to have their setup audited by third parties. Note though, that the Tendermint team will not approve or conduct any audit themselves. For more on due diligence, see [this blog post](https://medium.com/@interchain_io/3d0faf10ce6f)
## Responsibilites
### Do validators need to be publicly identified?
No, they do not. Each delegator will value validators based on their own criteria. Validators will be able (and are advised) to register a website address when they nominate themselves so that they can advertise their operation as they see fit. Some delegators may prefer a website that clearly displays the team running the validator and their resume, while others might prefer anonymous validators with positive track records. Most likely both identified and anonymous validators will coexist in the validator set.
No, they do not. Each delegator will value validators based on their own criteria. Validators will be able to register a website address when they nominate themselves so that they can advertise their operation as they see fit. Some delegators may prefer a website that clearly displays the team operating the validator and their resume, while others might prefer anonymous validators with positive track records.
### What are the responsiblities of a validator?
Validators have two main responsibilities:
* **Be able to constantly run a correct version of the software:** validators need to make sure that their servers are always online and their private keys are not compromised.
* **Actively participate in governance:** validators are required to vote on every proposal.
* **Be able to constantly run a correct version of the software:**Validators need to make sure that their servers are always online and their private keys are not compromised.
* **Actively participate in governance:** Validators are required to vote on every proposal.
Additionally, validators are expected to be active members of the community. They should always be up-to-date with the current state of the ecosystem so that they can easily adapt to any change.
### What does 'participate in governance' entail?
Validators and delegators on the Cosmos Hub can vote on proposals to change operational parameters (such as the block gas limit), coordinate upgrades, as well as vote on amendments to the human-readable constitution that govern the Cosmos Hub.
Validators and delegators on the Cosmos Hub can vote on proposals to change operational parameters (such as the block gas limit), coordinate upgrades, or make a decision on any given matter.
Validators play a special role in the governance system. Being the pillars of the system, they are required to vote on every proposal. It is especially important since delegators who do not vote will inherit the vote of their validator. Each time a validator does not vote on a proposal, it will get slashed by a minimal amount.
Validators play a special role in the governance system. Being the pillars of the system, they are required to vote on every proposal. It is especially important since delegators who do not vote will inherit the vote of their validator.
### What does staking imply?
Staking Atoms can be thought of as a safety deposit on validation activities. When a validator or a delegator wants to retrieve part or all of their deposit, they send an unbonding transaction. Then, Atoms undergo a _three weeks unbonding period_ during which they are liable to being slashed for potential misbehaviors committed by the validator before the unbonding process started.
Staking Atoms can be thought of as a safety deposit on validation activities. When a validator or a delegator wants to retrieve part or all of their deposit, they send an `unbonding` transaction. Then, Atoms undergo a **3 weeks unbonding period** during which they are liable to being slashed for potential misbehaviors committed by the validator before the unbonding process started.
Validators, and by association delegators, receive block provisions, block rewards, fee rewards, and the right to participate in governance. If a validator misbehaves, a certain portion of its total stake is slashed (the severity of the penalty depends on the type of misbehavior). This means that every user that bonded Atoms to this validator gets penalized in proportion to its stake. Delegators are therefore incentivized to delegate to validators that they anticipate will function safely.
Validators, and by association delegators, receive block rewards, fees, and have the right to participate in governance. If a validator misbehaves, a certain portion of their total stake is slashed. This means that every delegator that bonded Atoms to this validator gets penalized in proportion to their bonded stake. Delegators are therefore incentivized to delegate to validators that they anticipate will function safely.
### Can a validator run away with its delegators' Atoms?
### Can a validator run away with their delegators' Atoms?
By delegating to a validator, a user delegates staking power. The more staking power a validator has, the more weight it has in the consensus and governance processes. This does not mean that the validator has custody of its delegators' Atoms. _By no means can a validator run away with its delegator's funds_.
By delegating to a validator, a user delegates voting power. The more voting power a validator have, the more weight they have in the consensus and governance processes. This does not mean that the validator has custody of their delegators' Atoms. **By no means can a validator run away with its delegator's funds**.
Even though delegated funds cannot be stolen by their validators, delegators are still liable if their validators misbehave. In such case, each delegators' stake will be partially slashed in proportion to their relative stake.
Even though delegated funds cannot be stolen by their validators, delegators are still liable if their validators misbehave.
### How often will a validator be chosen to propose the next block? Does it go up with the quantity of Atoms staked?
### How often will a validator be chosen to propose the next block? Does it go up with the quantity of bonded Atoms?
The validator that is selected to propose the next block is called proposer. Each proposer is selected deterministically, and the frequency of being chosen is equal to the relative total stake (where total stake = self-bonded stake + delegators stake) of the validator. For example, if the total bonded stake across all validators is 100 Atoms and a validator's total stake is 10 Atoms, then this validator will be chosen 10% of the time as the next proposer.
The validator that is selected to propose the next block is called proposer. Each proposer is selected deterministically, and the frequency of being chosen is proportional to the voting power (i.e. amount of bonded Atoms) of the validator. For example, if the total bonded stake across all validators is 100 Atoms and a validator's total stake is 10 Atoms, then this validator will proposer ~10% of the blocks.
### Will validators of the Cosmos Hub ever be required to validate other zones in the Cosmos ecosystem?
Yes, they will. Initially, validators of the Cosmos hub will also validate the first public Ethermint zone. If governance decides so, validators of the Cosmos hub may be required to validate additional zones in the Cosmos ecosystem. As the case with the Ethermint Zone, for each additional zone compensation is to be provided in the form of block rewards and transaction fees.
Yes, they will. If governance decides so, validators of the Cosmos hub may be required to validate additional zones in the Cosmos ecosystem.
## Incentives
@ -167,45 +160,44 @@ Yes, they will. Initially, validators of the Cosmos hub will also validate the f
Each member of a validator's staking pool earns different types of revenue:
* **Block provisions:** Native tokens of applications run by validators (e.g. Atoms on the Cosmos Hub) are inflated to produce block provisions. These provisions exist to incentivize Atom holders to bond their stake, as non-bonded Atom will be diluted over time.
* **Block rewards:** For the Ethermint zone, block rewards are paid in Photons. Initial distribution of Photons will be hard spooned from Ethereum. This means Photons will be emitted 1:1 to Ether.
* **Transaction fees:** The Cosmos Hub maintains a whitelist of token that are accepted as fee payment.
* **Block rewards:** Native tokens of applications run by validators (e.g. Atoms on the Cosmos Hub) are inflated to produce block provisions. These provisions exist to incentivize Atom holders to bond their stake, as non-bonded Atom will be diluted over time.
* **Transaction fees:** The Cosmos Hub maintains a whitelist of token that are accepted as fee payment. The initial fee token is tha `atom`.
This total revenue is divided among validators' staking pools according to each validator's weight. Then, within each validator's staking pool the revenue is divided among delegators in proportion to each delegator's stake. Note that a commission on delegators' revenue is applied by the validator before it is distributed.
This total revenue is divided among validators' staking pools according to each validator's weight. Then, within each validator's staking pool the revenue is divided among delegators in proportion to each delegator's stake. A commission on delegators' revenue is applied by the validator before it is distributed.
### What is the incentive to run a validator ?
Validators earn proportionally more revenue than their delegators because of commissions.
Validators also play a major role in governance. If a delegator does not vote, it inherits the vote from its validator. This gives validators a major responsibility in the ecosystem.
Validators also play a major role in governance. If a delegator does not vote, they inherit the vote from their validator. This gives validators a major responsibility in the ecosystem.
### What is a validator's commission?
### What are validators commission?
Revenue received by a validator's pool is split between the validator and its delegators. The validator can apply a commission on the part of the revenue that goes to its delegators. This commission is set as a percentage. Each validator is free to set its initial commission, maximum daily commission change rate and maximum commission. The Cosmos Hub enforces the parameter that each validator sets. These parameters can only be defined when initially declaring candidacy, and may only be constrained further after being declared.
Revenue received by a validator's pool is split between the validator and their delegators. The validator can apply a commission on the part of the revenue that goes to their delegators. This commission is set as a percentage. Each validator is free to set their initial commission, maximum daily commission change rate and maximum commission. The Cosmos Hub enforces the parameter that each validator sets. Only the commission rate can change after the validator is created.
### How are block provisions distributed?
### How are block rewards distributed?
Block provisions are distributed proportionally to all validators relative to their total stake. This means that even though each validator gains atoms with each provision, all validators will still maintain equal weight.
Block rewards are distributed proportionally to all validators relative to their voting power. This means that even though each validator gains atoms with each reward, all validators will maintain equal weight over time.
Let us take an example where we have 10 validators with equal staking power and a commission rate of 1%. Let us also assume that the provision for a block is 1000 Atoms and that each validator has 20% of self-bonded Atoms. These tokens do not go directly to the proposer. Instead, they are evenly spread among validators. So now each validator's pool has 100 Atoms. These 100 Atoms will be distributed according to each participant's stake:
Let us take an example where we have 10 validators with equal voting power and a commission rate of 1%. Let us also assume that the reward for a block is 1000 Atoms and that each validator has 20% of self-bonded Atoms. These tokens do not go directly to the proposer. Instead, they are evenly spread among validators. So now each validator's pool has 100 Atoms. These 100 Atoms will be distributed according to each participant's stake:
* Commission: `100*80%*1% = 0.8 Atoms`
* Validator gets: `100\*20% + Commission = 20.8 Atoms`
* All delegators get: `100\*80% - Commission = 79.2 Atoms`
Then, each delegator can claim its part of the 79.2 Atoms in proportion to their stake in the validator's staking pool. Note that the validator's commission is not applied on block provisions. Note that block rewards (paid in Photons) are distributed according to the same mechanism.
Then, each delegator can claim their part of the 79.2 Atoms in proportion to their stake in the validator's staking pool.
### How are fees distributed?
Fees are similarly distributed with the exception that the block proposer can get a bonus on the fees of the block it proposes if it includes more than the strict minimum of required precommits.
Fees are similarly distributed with the exception that the block proposer can get a bonus on the fees of the block they propose if they include more than the strict minimum of required precommits.
When a validator is selected to propose the next block, it must include at least 2/3 precommits for the previous block in the form of validator signatures. However, there is an incentive to include more than 2/3 precommits in the form of a bonus. The bonus is linear: it ranges from 1% if the proposer includes 2/3rd precommits (minimum for the block to be valid) to 5% if the proposer includes 100% precommits. Of course the proposer should not wait too long or other validators may timeout and move on to the next proposer. As such, validators have to find a balance between wait-time to get the most signatures and risk of losing out on proposing the next block. This mechanism aims to incentivize non-empty block proposals, better networking between validators as well as to mitigate censorship.
When a validator is selected to propose the next block, they must include at least 2/3 precommits of the previous block. However, there is an incentive to include more than 2/3 precommits in the form of a bonus. The bonus is linear: it ranges from 1% if the proposer includes 2/3rd precommits (minimum for the block to be valid) to 5% if the proposer includes 100% precommits. Of course the proposer should not wait too long or other validators may timeout and move on to the next proposer. As such, validators have to find a balance between wait-time to get the most signatures and risk of losing out on proposing the next block. This mechanism aims to incentivize non-empty block proposals, better networking between validators as well as to mitigate censorship.
Let's take a concrete example to illustrate the aforementioned concept. In this example, there are 10 validators with equal stake. Each of them applies a 1% commission and has 20% of self-bonded Atoms. Now comes a successful block that collects a total of 1025.51020408 Atoms in fees.
Let's take a concrete example to illustrate the aforementioned concept. In this example, there are 10 validators with equal stake. Each of them applies a 1% commission rate and has 20% of self-delegated Atoms. Now comes a successful block that collects a total of 1025.51020408 Atoms in fees.
First, a 2% tax is applied. The corresponding Atoms go to the reserve pool. Reserve pool's funds can be allocated through governance to fund bounties and upgrades.
* `2% \* 1025.51020408 = 20.51020408` Atoms go to the reserve pool.
* `2% * 1025.51020408 = 20.51020408` Atoms go to the reserve pool.
1005 Atoms now remain. Let's assume that the proposer included 100% of the signatures in its block. It thus obtains the full bonus of 5%.
@ -222,32 +214,25 @@ We have to solve this simple equation to find the reward R for each validator:
* The pool obtains R: 100 Atoms
* Commission: `100 * 80% * 1%` = 0.8 Atoms
* Validator's reward: `100 * 20% + Commission` = 20.8 Atoms
* Delegators' rewards: `100 * 80% - Commission` = 79.2 Atoms (each delegator will be able to claim its portion of these rewards in proportion to their stake)
* Delegators' rewards: `100 * 80% - Commission` = 79.2 Atoms (each delegator will be able to claim their portion of these rewards in proportion to their stake)
### What are the slashing conditions?
If a validator misbehaves, its bonded stake along with its delegators' stake and will be slashed. The severity of the punishment depends on the type of fault. There are 3 main faults that can result in slashing of funds for a validator and its delegators:
If a validator misbehaves, their delegated stake will be partially slashed. There is currently one main fault that can result in slashing of funds for a validator and their delegators:
* **Double signing:** If someone reports on chain A that a validator signed two blocks at the same height on chain A and chain B, and if chain A and chain B share a common ancestor, then this validator will get slashed on chain A
* **Unavailability:** If a validator's signature has not been included in the last X blocks, the validator will get slashed by a marginal amount proportional to X. If X is above a certain limit Y, then the validator will get unbonded
* **Non-voting:** If a validator did not vote on a proposal, its stake will receive a minor slash.
Note that even if a validator does not intentionally misbehave, it can still be slashed if its node crashes, looses connectivity, gets DDOSed, or if its private key is compromised.
### Do validators need to self-delegate Atoms?
### Do validators need to self-bond Atoms?
Yes, they do need to seld-delegate at least `1 atom`. Even though there is no obligation for validators to self-delegate more than `1 atom`, delegators should want their validator to have more self-delegated Atoms in their staking pool. In other words, validators should have skin in the game.
No, they do not. A validators total stake is equal to the sum of its own self-bonded stake and of its delegated stake. This means that a validator can compensate its low amount of self-bonded stake by attracting more delegators. This is why reputation is very important for validators.
Even though there is no obligation for validators to self-bond Atoms, delegators should want their validator to have self-bonded Atoms in their staking pool. In other words, validators should have skin in the game.
In order for delegators to have some guarantee about how much skin-in-the-game their validator has, the latter can signal a minimum amount of self-bonded Atoms. If a validator's self-bond goes below the limit that it predefined, this validator and all of its delegators will unbond.
In order for delegators to have some guarantee about how much skin-in-the-game their validator has, the latter can signal a minimum amount of self-delegated Atoms. If a validator's self-delegation goes below the limit that it predefined, this validator and all of its delegators will unbond.
### How to prevent concentration of stake in the hands of a few top validators?
For now the community is expected to behave in a smart and self-preserving way. When a mining pool in Bitcoin gets too much mining power the community usually stops contributing to that pool. The Cosmos Hub will rely on the same effect initially. In the future, other mechanisms will be deployed to smoothen this process as much as possible:
For now the community is expected to behave in a smart and self-preserving way. When a mining pool in Bitcoin gets too much mining power the community usually stops contributing to that pool. The Cosmos Hub will rely on the same effect initially. Other mechanisms are in place to smoothen this process as much as possible:
* **Penalty-free re-delegation:** This is to allow delegators to easily switch from one validator to another, in order to reduce validator stickiness.
* **Hack bounty:** This is an incentive for the community to hack validators. There will be bounties proportionate to the size of the validator, so that a validator becomes a bigger target as its stake grows.
* **UI warning:** Users will be warned by Cosmos Voyager if they want to delegate to a validator that already has a significant amount of staking power.
## Technical Requirements

View File

@ -53,20 +53,19 @@ __Note__: If unspecified, `consensus_pubkey` will default to the output of `gaia
## Participate in genesis as a validator
__Note__: This section only concerns validators that want to be in the genesis file. If the chain you want to validate is already live, skip this section.
__Note__: This section only concerns validators that want to be in the genesis
file. If the chain you want to validate is already live, skip this section.
__Note__: `Gaia-9002` and `Game of stakes` will not use this process. They will be bootsrapped using Tendermint seed validators. You will just need to use the [create-validator](#create-your-validator) command in order to join as a validator for these networks.
__Note__: `Gaia-9002` and `Game of stakes` will not use this process. They will
be bootstrapped using validators operated by Tendermint. You will just need to use the
[create-validator](#create-your-validator) command in order to join as a validator
for these networks.
If you want to participate in genesis as a validator, you need to justify that you (or a delegator) have some stake at genesis, create one (or multiple) transaction to bond this stake to your validator address, and include this transaction in the genesis file.
If you want to participate in genesis as a validator, you need to justify that
you have some stake at genesis, create one (or multiple) transactions to bond this
stake to your validator address, and include this transaction in the genesis file.
We thus need to distinguish two cases:
- Case 1: You want to bond the initial stake from your validator's address.
- Case 2: You want to bond the initial stake from a delegator's address.
### Case 1: The initial stake comes from your validator's address
In this case, you will create a `gentx`:
You will need create a `gentx`:
```bash
gaiad gentx \
@ -78,46 +77,17 @@ gaiad gentx \
--name <key_name>
```
__Note__: This command automatically store your `gentx` in `~/.gaiad/config/gentx` for it to be processed at genesis.
__Note__: This command automatically store your `gentx` in `~/.gaiad/config/gentx`
for it to be processed at genesis.
::: tip
Consult `gaiad gentx --help` for more information on the flags defaults.
:::
A `gentx` is a JSON file carrying a self-delegation. All genesis transactions are collected by a `genesis coordinator` and validated against an initial `genesis.json`. Such initial `genesis.json` contains only a list of accounts and their coins. Once the transactions are processed, they are merged in the `genesis.json`'s `gentxs` field.
### Case 2: The initial stake comes from a delegator's address
In this case, you need both the signature of the validator and the delegator. Start by creating an unsigned `create-validator` transaction, and save it in a file called `unsignedValTx`:
```bash
gaiacli tx staking create-validator \
--amount=5STAKE \
--pubkey=$(gaiad tendermint show-validator) \
--moniker="choose a moniker" \
--chain-id=<chain_id> \
--from=<key_name> \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--address-delegator="address of the delegator" \
--generate-only \
> unsignedValTx.json
```
Then, sign this `unsignedValTx` with your validator's private key, and save the output in a new file `signedValTx.json`:
```bash
gaiacli tx sign unsignedValTx.json --from=<validator_key_name> > signedValTx.json
```
Then, pass this file to the delegator, who needs to run the following command:
```bash
gaiacli tx sign signedValTx.json --from=<delegator_key_name> > gentx.json
```
This `gentx.json` needs to be included in the `~/.gaiad/config/gentx` folder on the validator's machine to be processed at genesis, just like in case 1 (except here it needs to be copied manually into the folder).
A `gentx` is a JSON file carrying a self-delegation. All genesis transactions are
collected by a `genesis coordinator` and validated against an initial `genesis.json`.
Such initial `genesis.json` contains only a list of accounts and their coins.
Once the transactions are processed, they are merged in the `genesis.json`'s `gentxs` field.
### Copy the Initial Genesis File and Process Genesis Transactions

View File

@ -7,20 +7,35 @@ this directory.
For consistency, specs should be written in passive present tense.
## Pseudo-Code
Generally, pseudo-code should be minimized throughout the spec. Often, simple
bulleted-lists which describe a function's operations are sufficient and should
be considered preferable. In certain instances, due to the complex nature of
the functionality being described pseudo-code may the most suitable form of
specification. In these cases use of pseudo-code is permissible, but should be
presented in a concise manner, ideally restricted to only the complex
element as a part of a larger description.
## Common Layout
The following generalized structure should be used to breakdown specifications
for modules. Note that not all files may be required depending on the modules
function
The following generalized file structure should be used to breakdown
specifications for modules. With the exception of README.md, `XX` at the
beginning of the file name should be replaced with a number to indicate
document flow (ex. read `01_state.md` before `02_state_transitions.md`). The
following list is nonbinding and all files are optional.
- `overview.md` - describe module
- `state.md` - specify and describe structures expected to marshalled into the store, and their keys
- `state_transitions.md` - standard state transition operations triggered by hooks, messages, etc.
- `end_block.md` - specify any end-block operations
- `begin_block.md` - specify any begin-block operations
- `messages.md` - specify message structure and expected state machine behaviour
- `hooks.md` - describe available hooks to be called by/from this module
- `tags.md` - list and describe event tags used
- `README.md` - overview of the module
- `XX_concepts.md` - describe specialized concepts and definitions used throughout the spec
- `XX_state.md` - specify and describe structures expected to marshalled into the store, and their keys
- `XX_state_transitions.md` - standard state transition operations triggered by hooks, messages, etc.
- `XX_messages.md` - specify message structure(s) and expected state machine behaviour(s)
- `XX_begin_block.md` - specify any begin-block operations
- `XX_end_block.md` - specify any end-block operations
- `XX_hooks.md` - describe available hooks to be called by/from this module
- `XX_tags.md` - list and describe event tags used
- `XX_future_improvements.md` - describe future improvements of this module
- `XX_appendix.md` - supplementary details referenced elsewhere within the spec
### Notation for key-value mapping

View File

@ -1,4 +1,6 @@
# Gas & Fees
# Concepts
## Gas & Fees
Fees serve two purposes for an operator of the network.

View File

@ -1,6 +1,6 @@
## State
# State
### Accounts
## Accounts
Accounts contain authentication information for a uniquely identified external user of an SDK blockchain,
including public key, address, and account number / sequence number for replay protection. For efficiency,
@ -13,7 +13,7 @@ account types may do so.
- `0x01 | Address -> amino(account)`
#### Account Interface
### Account Interface
The account interface exposes methods to read and write standard account information.
Note that all of these methods operate on an account struct confirming to the interface
@ -53,6 +53,6 @@ type BaseAccount struct {
}
```
#### Vesting Account
### Vesting Account
See [Vesting](vesting.md).

View File

@ -1,3 +1,7 @@
# Messages
TODO make this file conform to typical messages spec
## Handlers
The auth module presently has no transaction handlers of its own, but does expose

View File

@ -1,4 +1,4 @@
## Types
# Types
Besides accounts (specified in [State](state.md)), the types exposed by the auth module
are `StdFee`, the combination of an amount and gas limit, `StdSignature`, the combination
@ -6,7 +6,7 @@ of an optional public key and a cryptographic signature as a byte array, `StdTx`
a struct which implements the `sdk.Tx` interface using `StdFee` and `StdSignature`, and
`StdSignDoc`, a replay-prevention structure for `StdTx` which transaction senders must sign over.
### StdFee
## StdFee
A `StdFee` is simply the combination of a fee amount, in any number of denominations,
and a gas limit (where dividing the amount by the gas limit gives a "gas price").
@ -18,7 +18,7 @@ type StdFee struct {
}
```
### StdSignature
## StdSignature
A `StdSignature` is the combination of an optional public key and a cryptographic signature
as a byte array. The SDK is agnostic to particular key or signature formats and supports any
@ -31,7 +31,7 @@ type StdSignature struct {
}
```
### StdTx
## StdTx
A `StdTx` is a struct which implements the `sdk.Tx` interface, and is likely to be generic
enough to serve the purposes of many Cosmos SDK blockchains.
@ -45,7 +45,7 @@ type StdTx struct {
}
```
### StdSignDoc
## StdSignDoc
A `StdSignDoc` is a replay-prevention structure to be signed over, which ensures that
any submitted transaction (which is simply a signature over a particular bytestring)

View File

@ -1,8 +1,8 @@
## Keepers
# Keepers
The auth module only exposes one keeper, the account keeper, which can be used to read and write accounts.
### Account Keeper
## Account Keeper
Presently only one fully-permissioned account keeper is exposed, which has the ability to both read and write
all fields of all accounts, and to iterate over all stored accounts.

View File

@ -251,10 +251,12 @@ func DelegateCoins(t Time, from Account, amount Coins) {
### Undelegating
For a vesting account attempting to undelegate `D` coins, the following is performed:
NOTE: `DV < D` and `(DV + DF) < D` may be possible due to quirks in the rounding of
delegation/undelegation logic.
1. Verify `(DV + DF) >= D > 0` (this is simply a sanity check)
1. Verify `D > 0`
2. Compute `X := min(DF, D)` (portion of `D` that should become free, prioritizing free coins)
3. Compute `Y := D - X` (portion of `D` that should remain vesting)
3. Compute `Y := min(DV, D - X)` (portion of `D` that should remain vesting)
4. Set `DF -= X`
5. Set `DV -= Y`
6. Set `BC += D`
@ -274,6 +276,11 @@ func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) {
with an excess `DV` amount, even after all its coins have vested. This is because
undelegating free coins are prioritized.
**Note**: The undelegation (bond refund) amount may exceed the delegated
vesting (bond) amount due to the way undelegation truncates the bond refund,
which can increase the validator's exchange rate (tokens/shares) slightly if the
undelegated tokens are non-integral.
#### Keepers/Handlers
```go

View File

@ -13,18 +13,24 @@ This module will be used in the Cosmos Hub.
## Contents
1. **[State](state.md)**
1. [Accounts](state.md#accounts)
1. [Account Interface](state.md#account-interface)
2. [Base Account](state.md#baseaccount)
3. [Vesting Account](state.md#vestingaccount)
1. **[Types](types.md)**
1. [StdFee](types.md#stdfee)
2. [StdSignature](types.md#stdsignature)
3. [StdTx](types.md#stdtx)
4. [StdSignDoc](types.md#stdsigndoc)
1. **[Keepers](keepers.md)**
1. [AccountKeeper](keepers.md#account-keeper)
1. **[Handlers](handlers.md)**
1. [Ante Handler](handlers.md#ante-handler)
1. **[Gas & Fees](gas_fees.md)**
1. **[Concepts](01_concepts.md)**
- [Gas & Fees](01_concepts.md#gas-&-fees)
2. **[State](02_state.md)**
- [Accounts](02_state.md#accounts)
3. **[Messages](03_messages.md)**
- [Handlers](03_messages.md#handlers)
4. **[Types](03_types.md)**
- [StdFee](03_types.md#stdfee)
- [StdSignature](03_types.md#stdsignature)
- [StdTx](03_types.md#stdtx)
- [StdSignDoc](03_types.md#stdsigndoc)
5. **[Keepers](04_keepers.md)**
- [Account Keeper](04_keepers.md#account-keeper)
6. **[Vesting](05_vesting.md)**
- [Intro and Requirements](05_vesting.md#intro-and-requirements)
- [Vesting Account Types](05_vesting.md#vesting-account-types)
- [Vesting Account Specification](05_vesting.md#vesting-account-specification)
- [Keepers & Handlers](05_vesting.md#keepers-&-handlers)
- [Genesis Initialization](05_vesting.md#genesis-initialization)
- [Examples](05_vesting.md#examples)
- [Glossary](05_vesting.md#glossary)

View File

@ -1,4 +1,4 @@
## State
# State
Presently, the bank module has no inherent state — it simply reads and writes accounts using the `AccountKeeper` from the `auth` module.

View File

@ -1,12 +1,12 @@
## Keepers
# Keepers
The bank module provides three different exported keeper interfaces which can be passed to other modules which need to read or update account balances. Modules should use the least-permissive interface which provides the functionality they require.
Note that you should always review the `bank` module code to ensure that permissions are limited in the way that you expect.
### Common Types
## Common Types
#### Input
### Input
An input of a multiparty transfer
@ -17,7 +17,7 @@ type Input struct {
}
```
#### Output
### Output
An output of a multiparty transfer.
@ -28,7 +28,7 @@ type Output struct {
}
```
### BaseKeeper
## BaseKeeper
The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins.
@ -82,7 +82,7 @@ inputOutputCoins(inputs []Input, outputs []Output)
addCoins(output.Address, output.Coins)
```
### SendKeeper
## SendKeeper
The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins).
@ -100,7 +100,7 @@ sendCoins(from AccAddress, to AccAddress, amt Coins)
addCoins(to, amt)
```
### ViewKeeper
## ViewKeeper
The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`.

View File

@ -1,6 +1,6 @@
## Messages
# Messages
### MsgSend
## MsgSend
```golang
type MsgSend struct {

View File

@ -14,13 +14,13 @@ This module will be used in the Cosmos Hub.
## Contents
1. **[State](state.md)**
1. **[Keepers](keepers.md)**
1. [Common Types](keepers.md#common-types)
1. [Input](keepers.md#input)
1. [Output](keepers.md#output)
1. [BaseKeeper](keepers.md#basekeeper)
1. [SendKeeper](keepers.md#sendkeeper)
1. [ViewKeeper](keepers.md#viewkeeper)
1. **[Transactions](transactions.md)**
1. [MsgSend](transactions.md#msgsend)
1. **[State](01_state.md)**
2. **[Keepers](02_keepers.md)**
- [Common Types](02_keepers.md#common-types)
- [BaseKeeper](02_keepers.md#basekeeper)
- [SendKeeper](02_keepers.md#sendkeeper)
- [ViewKeeper](02_keepers.md#viewkeeper)
3. **[Messages](03_messages.md)**
- [MsgSend](03_messages.md#msgsend)
4. **[Tags](04_tags.md)**
- [Handlers](04_tags.md#handlers)

View File

@ -1,3 +1,5 @@
# Concepts
## Reference Counting in F1 Fee Distribution
In F1 fee distribution, in order to calculate the rewards a delegator ought to receive when they

View File

@ -74,3 +74,22 @@ In conclusion, we can only have Atom commission and unbonded atoms
provisions or bonded atom provisions with no Atom commission, and we elect to
implement the former. Stakeholders wishing to rebond their provisions may elect
to set up a script to periodically withdraw and rebond rewards.
## Contents
1. **[Concepts](01_concepts.md)**
- [Reference Counting in F1 Fee Distribution](01_concepts.md#reference-counting-in-f1-fee-distribution)
2. **[02_state.md](02_state.md)**
- [State](02_state.md#state)
3. **[End Block](03_end_block.md)**
4. **[Messages](04_messages.md)**
- [MsgWithdrawDelegationRewardsAll](04_messages.md#msgwithdrawdelegationrewardsall)
- [MsgWithdrawDelegationReward](04_messages.md#msgwithdrawdelegationreward)
- [MsgWithdrawValidatorRewardsAll](04_messages.md#msgwithdrawvalidatorrewardsall)
- [Common calculations ](04_messages.md#common-calculations-)
5. **[Hooks](05_hooks.md)**
- [Create or modify delegation distribution](05_hooks.md#create-or-modify-delegation-distribution)
- [Commission rate change](05_hooks.md#commission-rate-change)
- [Change in Validator State](05_hooks.md#change-in-validator-state)
6. **[Tags](06_tags.md)**
- [Handlers](06_tags.md#handlers)

View File

@ -1,4 +1,4 @@
# Design Overview
# Concepts
*Disclaimer: This is work in progress. Mechanisms are susceptible to change.*
@ -139,20 +139,7 @@ If a delegator does not vote, it will inherit its validator vote.
### Validators punishment for non-voting
Validators are required to vote on all proposals to ensure that results have
legitimacy. Voting is part of validators' directives and failure to do it will
result in a penalty.
If a validators address is not in the list of addresses that voted on a
proposal and the vote is closed (i.e. `MinDeposit` was reached and `Voting
period` is over), then the validator will automatically be partially slashed by
`GovernancePenalty`.
*Note: Need to define values for `GovernancePenalty`*
**Exception:** If a proposal is accepted via the special condition of having a ratio of `Yes` votes to `InitTotalVotingPower` that exceeds 2:3, validators cannot be punished for not having voted on it.
That is because the proposal will close as soon as the ratio exceeds 2:3,
making it mechanically impossible for some validators to vote on it.
At present, validators are not punished for failing to vote.
### Governance address
@ -185,4 +172,4 @@ Once a block contains more than 2/3rd *precommits* where a common
nodes, non-validating full nodes and light-nodes) are expected to switch to the
new version of the software.
*Note: Not clear how the flip is handled programatically*
*Note: Not clear how the flip is handled programatically*

View File

@ -1,8 +1,6 @@
# Implementation (1/2)
# State
## State
### Parameters and base types
## Parameters and base types
`Parameters` define the rules according to which votes are run. There can only
be one active parameter set at any given time. If governance wants to change a
@ -27,7 +25,6 @@ type TallyParams struct {
Quorum sdk.Dec // Minimum percentage of stake that needs to vote for a proposal to be considered valid
Threshold sdk.Dec // Minimum proportion 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
}
```
@ -65,7 +62,7 @@ const (
)
```
### Deposit
## Deposit
```go
type Deposit struct {
@ -74,7 +71,7 @@ const (
}
```
### ValidatorGovInfo
## ValidatorGovInfo
This type is used in a temp map when tallying
@ -85,7 +82,7 @@ This type is used in a temp map when tallying
}
```
### Proposals
## Proposals
`Proposals` are an item to be voted on.
@ -117,7 +114,7 @@ We also mention a method to update the tally for a given proposal:
func (proposal Proposal) updateTally(vote byte, amount sdk.Dec)
```
### Stores
## Stores
*Stores are KVStores in the multistore. The key to find the store is the first parameter in the list*`
@ -132,7 +129,7 @@ For pseudocode purposes, here are the two function we will use to read or write
* `load(StoreKey, Key)`: Retrieve item stored at key `Key` in store found at key `StoreKey` in the multistore
* `store(StoreKey, Key, value)`: Write value `Value` at key `Key` in store found at key `StoreKey` in the multistore
### Proposal Processing Queue
## Proposal Processing Queue
**Store:**
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the

View File

@ -1,8 +1,6 @@
# Implementation (2/2)
# Messages
## Messages
### Proposal Submission
## Proposal Submission
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
transaction.
@ -69,7 +67,7 @@ upon receiving txGovSubmitProposal from sender do
return proposalID
```
### Deposit
## Deposit
Once a proposal is submitted, if
`Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send
@ -138,7 +136,7 @@ upon receiving txGovDeposit from sender do
store(Proposals, <txGovVote.ProposalID|'proposal'>, proposal)
```
### Vote
## Vote
Once `ActiveParam.MinDeposit` is reached, voting period starts. From there,
bonded Atom holders are able to send `TxGovVote` transactions to cast their

View File

@ -1,4 +1,4 @@
# Future improvements (not in scope for MVP)
# Future Improvements
The current documentation only describes the minimum viable product for the
governance module. Future improvements may include:
@ -27,4 +27,4 @@ governance module. Future improvements may include:
process.
* **Better process for proposal review:** There would be two parts to
`proposal.Deposit`, one for anti-spam (same as in MVP) and an other one to
reward third party auditors.
reward third party auditors.

View File

@ -21,15 +21,22 @@ This module will be used in the Cosmos Hub, the first Hub in the Cosmos network.
The following specification uses *Atom* as the native staking token. The module can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the native staking token of the chain.
1. **[Design overview](overview.md)**
2. **Implementation**
1. **[State](state.md)**
1. Parameters
2. Proposals
3. Proposal Processing Queue
2. **[Transactions](transactions.md)**
1. Proposal Submission
2. Deposit
3. Claim Deposit
4. Vote
3. **[Future improvements](future_improvements.md)**
1. **[Concepts](01_concepts.md)**
- [Proposal submission](01_concepts.md#proposal-submission)
- [Vote](01_concepts.md#vote)
- [Software Upgrade](01_concepts.md#software-upgrade)
2. **[State](02_state.md)**
- [Parameters and base types](02_state.md#parameters-and-base-types)
- [Deposit](02_state.md#deposit)
- [ValidatorGovInfo](02_state.md#validatorgovinfo)
- [Proposals](02_state.md#proposals)
- [Stores](02_state.md#stores)
- [Proposal Processing Queue](02_state.md#proposal-processing-queue)
3. **[Messages](03_messages.md)**
- [Proposal Submission](03_messages.md#proposal-submission)
- [Deposit](03_messages.md#deposit)
- [Vote](03_messages.md#vote)
4. **[Tags](04_tags.md)**
- [EndBlocker](04_tags.md#endblocker)
- [Handlers](04_tags.md#handlers)
5. **[Future Improvements](05_future_improvements.md)**

View File

@ -26,12 +26,12 @@ type IBCPacket struct {
}
// Implements sdk.Msg
type IBCTransferMsg struct {
type MsgIBCTransfer struct {
IBCPacket
}
// Implements sdk.Msg
type IBCReceiveMsg struct {
type MsgIBCReceive struct {
IBCPacket
Relayer sdk.Address
Sequence int64

View File

@ -29,12 +29,12 @@ type TransferPayload struct {
}
// Implements sdk.Msg
type IBCTransferMsg struct {
type MsgIBCTransfer struct {
Packet
}
// Implements sdk.Msg
type IBCReceiveMsg struct {
type MsgIBCReceive struct {
Packet
Relayer sdk.Address
Sequence int64

View File

@ -8,12 +8,12 @@
// Implements sdk.Msg
type IBCTransferMsg struct {
type MsgIBCTransfer struct {
Packet
}
// Implements sdk.Msg
type IBCReceiveMsg struct {
type MsgIBCReceive struct {
Packet
}
@ -42,12 +42,12 @@ type TransferPayload struct {
}
// Implements sdk.Msg
type IBCTransferMsg struct {
type MsgIBCTransfer struct {
Packet
}
// Implements sdk.Msg
type IBCReceiveMsg struct {
type MsgIBCReceive struct {
Packet
Proof iavl.Proof
FromChainID string

View File

@ -1,6 +1,6 @@
## State
# State
### Minter
## Minter
The minter is a space for holding current inflation information.
@ -13,7 +13,7 @@ type Minter struct {
}
```
### Params
## Params
Minting params are held in the global params store.

View File

@ -1,4 +1,11 @@
# Mint Specification
- [State](./state.md)
- [Begin Block](./begin_block.md)
## Contents
1. **[State](01_state.md)**
- [Minter](01_state.md#minter)
- [Params](01_state.md#params)
2. **[Begin-Block](02_begin_block.md)**
- [NextInflationRate](02_begin_block.md#nextinflationrate)
- [NextAnnualProvisions](02_begin_block.md#nextannualprovisions)
- [BlockProvision](02_begin_block.md#blockprovision)

Some files were not shown because too many files have changed in this diff Show More