Release v0.33.0
This commit is contained in:
commit
7b4104aced
|
@ -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:
|
||||
|
|
121
CHANGELOG.md
121
CHANGELOG.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
76
Makefile
76
Makefile
|
@ -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
|
||||
|
|
55
PENDING.md
55
PENDING.md
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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, "", " ")
|
||||
|
|
|
@ -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)
|
||||
if err != nil {
|
||||
_, 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 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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[:]))
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
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.`,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,111 +803,134 @@ 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,
|
||||
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
|
||||
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),
|
||||
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
|
||||
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),
|
||||
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
|
||||
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"`
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"`
|
||||
// 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]
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -161,9 +161,9 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest
|
|||
VotingPeriod: vp,
|
||||
},
|
||||
TallyParams: gov.TallyParams{
|
||||
Quorum: sdk.NewDecWithPrec(334, 3),
|
||||
Threshold: sdk.NewDecWithPrec(5, 1),
|
||||
Veto: sdk.NewDecWithPrec(334, 3),
|
||||
GovernancePenalty: sdk.NewDecWithPrec(1, 2),
|
||||
},
|
||||
}
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)})
|
||||
}
|
|
@ -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++
|
||||
}
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -112,12 +112,17 @@ func runPubKeyCmd(cmd *cobra.Command, args []string) error {
|
|||
pubKeyI, err4 = sdk.GetValPubKeyBech32(pubkeyString)
|
||||
|
||||
if err4 != nil {
|
||||
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
|
||||
`, err, err2, err3, err4)
|
||||
bech32 Acc: %v
|
||||
bech32 Val: %v
|
||||
bech32 Cons: %v`,
|
||||
err, err2, err3, err4, err5)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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旨在建立所有区块链间的桥梁,最终建立一个区块链互联网。然而,路漫漫其修远兮,我们还有很长的路要走。主网上线之后,我们需要完成与整个通证经济世界的深度融合,很多的工作要做等着我们去完成。正如肯尼迪在逆境中曾经说过的那样:
|
||||
|
||||
|
||||
|
||||
“我们选择去月球,不是因为很容易,而是因为很难......”
|
||||
|
||||
|
||||
|
||||
走更崎岖的路,才会看见更美的风景!
|
||||
****
|
|
@ -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. We’ve seen the community practice these techniques numerous times on the test networks. It’s 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 network’s 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]
|
|
@ -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 수집 작업을 시작합니다.
|
||||
|
||||
# 글을 마무리 하며
|
||||
|
||||
코스모스의 목적은 모든 블록체인을 연결하여 ‘블록체인의 인터넷’을 만드는 것입니다.
|
||||
|
||||
앞으로 가야 할 길이 멀다는 것을 알고 있습니다.
|
||||
|
||||
메인넷이 런칭된 후, 토큰 이코노미 기반의 세상을 설계하는 본격적인 일이 본격적으로 시작돼야 할 것입니다.
|
||||
|
||||
하지만 미국 전 대통령 존 케네디의 말을 인용하자면, “우리는 달에 가기로 결정하였습니다. 그것이 쉽기 때문이 아니라 어렵기 때문에 이렇게 결정한 것입니다.”
|
||||
|
||||
달에 도착하는 그 날까지… 🚀
|
|
@ -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 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`
|
||||
- **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`.
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# Gas & Fees
|
||||
# Concepts
|
||||
|
||||
## Gas & Fees
|
||||
|
||||
Fees serve two purposes for an operator of the network.
|
||||
|
|
@ -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).
|
|
@ -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
|
|
@ -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)
|
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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)`.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
## Messages
|
||||
# Messages
|
||||
|
||||
### MsgSend
|
||||
## MsgSend
|
||||
|
||||
```golang
|
||||
type MsgSend struct {
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
|||
|
||||
### Validator’s 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 validator’s 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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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)**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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
Loading…
Reference in New Issue