diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d753a6d..5d8864201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 0.23.1 + +*July 27th, 2018* + +BUG FIXES + * [tendermint] Update to v0.22.8 + - [consensus, blockchain] Register the Evidence interface so it can be + marshalled/unmarshalled by the blockchain and consensus reactors + +## 0.23.0 + +*July 25th, 2018* + +BREAKING CHANGES +* [x/stake] Fixed the period check for the inflation calculation + +IMPROVEMENTS +* [cli] Improve error messages for all txs when the account doesn't exist +* [tendermint] Update to v0.22.6 + - Updates the crypto imports/API (#1966) +* [x/stake] Add revoked to human-readable validator + +BUG FIXES +* [tendermint] Update to v0.22.6 + - Fixes some security vulnerabilities reported in the [Bug Bounty](https://hackerone.com/tendermint) +* \#1797 Fix off-by-one error in slashing for downtime +* \#1787 Fixed bug where Tally fails due to revoked/unbonding validator +* \#1666 Add intra-tx counter to the genesis validators + ## 0.22.0 *July 16th, 2018* diff --git a/Gopkg.lock b/Gopkg.lock index c69af1c2e..de244c162 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,11 +34,11 @@ [[projects]] branch = "master" - digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79" + digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "cf05f92c3f815bbd5091ed6c73eff51f7b1945e8" + revision = "f899737d7f2764dc13e4d01ff00108ec58f766a9" [[projects]] digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" @@ -71,7 +71,7 @@ version = "v1.4.7" [[projects]] - digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe" + digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" name = "github.com/go-kit/kit" packages = [ "log", @@ -103,7 +103,7 @@ version = "v1.7.0" [[projects]] - digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45" + digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -118,7 +118,7 @@ version = "v1.1.1" [[projects]] - digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888" + digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" name = "github.com/golang/protobuf" packages = [ "proto", @@ -165,7 +165,7 @@ [[projects]] branch = "master" - digest = "1:ac64f01acc5eeea9dde40e326de6b6471e501392ec06524c3b51033aa50789bc" + digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -263,7 +263,7 @@ version = "v1.0.0" [[projects]] - digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e" + digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", @@ -274,7 +274,7 @@ [[projects]] branch = "master" - digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "UT" @@ -282,7 +282,7 @@ [[projects]] branch = "master" - digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3" + digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -294,7 +294,7 @@ [[projects]] branch = "master" - digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd" + digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" name = "github.com/prometheus/procfs" packages = [ ".", @@ -313,7 +313,7 @@ revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c" + digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" name = "github.com/spf13/afero" packages = [ ".", @@ -332,7 +332,7 @@ version = "v1.2.0" [[projects]] - digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26" + digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "UT" @@ -341,19 +341,19 @@ [[projects]] branch = "master" - digest = "1:080e5f630945ad754f4b920e60b4d3095ba0237ebf88dc462eb28002932e3805" + digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a" name = "github.com/spf13/jwalterweatherman" packages = ["."] pruneopts = "UT" - revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" + revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" [[projects]] - digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7" + digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" name = "github.com/spf13/pflag" packages = ["."] pruneopts = "UT" - revision = "583c0c0531f06d5278b7d917446061adc344b5cd" - version = "v1.0.1" + revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" + version = "v1.0.2" [[projects]] digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" @@ -364,7 +364,7 @@ version = "v1.0.0" [[projects]] - digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d" + digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" name = "github.com/stretchr/testify" packages = [ "assert", @@ -376,7 +376,7 @@ [[projects]] branch = "master" - digest = "1:922191411ad8f61bcd8018ac127589bb489712c1d1a0ab2497aca4b16de417d2" + digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -397,7 +397,7 @@ [[projects]] branch = "master" - digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232" + digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" name = "github.com/tendermint/ed25519" packages = [ ".", @@ -424,7 +424,7 @@ version = "v0.9.2" [[projects]] - digest = "1:049c779b867a182cea567c65d7c81e3b9e4e4a7eece4c35a19639f75d2aa7da9" + digest = "1:26146cdb2811ce481e72138439b9b1aa17a64d54364f96bb92f97a9ef8ba4f01" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -487,23 +487,26 @@ "version", ] pruneopts = "UT" - revision = "5fdbcd70df57b71ffba71e1ff5f00d617852a9c0" - version = "v0.22.6" + revision = "013b9cef642f875634c614019ab13b17570778ad" + version = "v0.23.0" [[projects]] - digest = "1:5bd938386bd1f61a581bf8cd6ff2b7b2f79c542929176db4ceb44965440dae07" + digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" name = "github.com/zondax/ledger-goclient" packages = ["."] pruneopts = "UT" - revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b" + revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [[projects]] branch = "master" - digest = "1:e8206c1653e050116ec8c9a823a86413fc9f9ee3c2f3ae977c96d6a1747f7325" + digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5" name = "golang.org/x/crypto" packages = [ "blowfish", + "chacha20poly1305", "curve25519", + "hkdf", + "internal/chacha20", "internal/subtle", "nacl/box", "nacl/secretbox", @@ -515,10 +518,10 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "f027049dab0ad238e394a753dba2d14753473a04" + revision = "de0752318171da717af4ce24d0a2e8626afaeb11" [[projects]] - digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9" + digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -535,14 +538,17 @@ [[projects]] branch = "master" - digest = "1:4d7a8265af700258feaff86722049eb5b787240d66dfaf45ff4962f09de6e0be" + digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54" name = "golang.org/x/sys" - packages = ["unix"] + packages = [ + "cpu", + "unix", + ] pruneopts = "UT" - revision = "acbc56fc7007d2a01796d5bde54f39e3b3e95945" + revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" [[projects]] - digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca" + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -570,10 +576,10 @@ name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "UT" - revision = "daca94659cb50e9f37c1b834680f2e46358f10b0" + revision = "383e8b2c3b9e36c4076b235b32537292176bae20" [[projects]] - digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac" + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", diff --git a/Gopkg.toml b/Gopkg.toml index cab580e05..0651fe1f5 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -57,7 +57,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "=v0.22.6" + version = "=v0.23.0" [[constraint]] name = "github.com/bartekn/go-bip39" @@ -65,7 +65,7 @@ [[constraint]] name = "github.com/zondax/ledger-goclient" - revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b" + revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [prune] go-tests = true diff --git a/Makefile b/Makefile index 208b59ade..28b4256fb 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ BUILD_TAGS = netgo ledger BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}" GCC := $(shell command -v gcc 2> /dev/null) LEDGER_ENABLED ?= true +UNAME_S := $(shell uname -s) all: get_tools get_vendor_deps install install_examples install_cosmos-sdk-cli test_lint test ######################################## @@ -17,12 +18,18 @@ ci: get_tools get_vendor_deps install test_cover test_lint test check-ledger: ifeq ($(LEDGER_ENABLED),true) -ifndef GCC -$(error "gcc not installed for ledger support, please install") -endif + ifeq ($(UNAME_S),OpenBSD) + $(info "OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)") + TMP_BUILD_TAGS := $(BUILD_TAGS) + BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) + else + ifndef GCC + $(error "gcc not installed for ledger support, please install or set LEDGER_ENABLED to false in the Makefile") + endif + endif else -TMP_BUILD_TAGS := $(BUILD_TAGS) -BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) + TMP_BUILD_TAGS := $(BUILD_TAGS) + BUILD_TAGS = $(filter-out ledger, $(TMP_BUILD_TAGS)) endif build: check-ledger @@ -100,6 +107,7 @@ get_dev_tools: get_vendor_deps: @echo "--> Running dep ensure" + @rm -rf .vendor-new @dep ensure -v draw_deps: diff --git a/PENDING.md b/PENDING.md index c0971cf80..37da207a5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -1,8 +1,15 @@ +## v0.24.0 PENDING +^--- PENDING wasn't purged on sdk v0.23.0 release. + +BREAKING CHANGES +* Update to tendermint v0.23.0. This involves removing crypto.Pubkey, +maintaining a validator address to pubkey map, and using time.Time instead of int64 for time. [SDK PR](https://github.com/cosmos/cosmos-sdk/pull/1927) + ## PENDING BREAKING CHANGES * API - - \#1880 [x/stake] changed the endpoints to be more REST-ful + - \#1880 and \#2000 [x/stake] changed the endpoints to be more REST-ful * Update to tendermint v0.22.5. This involves changing all of the cryptography imports. [Ref](https://github.com/tendermint/tendermint/pull/1966) * [baseapp] Msgs are no longer run on CheckTx, removed `ctx.IsCheckTx()` * [x/gov] CLI flag changed from `proposalID` to `proposal-id` @@ -25,12 +32,14 @@ BREAKING CHANGES * `gaiacli gov submit-proposal --proposer` * `gaiacli gov deposit --depositer` * `gaiacli gov vote --voter` -* [x/gov] Added tags sub-package, changed tags to use dash-case +* [x/gov] Added tags sub-package, changed tags to use dash-case * [x/gov] Governance parameters are now stored in globalparams store +* [core] \#1807 Switch from use of rational to decimal * [lcd] \#1866 Updated lcd /slashing/signing_info endpoint to take cosmosvalpub instead of cosmosvaladdr * [types] sdk.NewCoin now takes sdk.Int, sdk.NewInt64Coin takes int64 * [cli] #1551: Officially removed `--name` from CLI commands * [cli] Genesis/key creation (`init`) now supports user-provided key passwords +* [cli] unsafe_reset_all, show_validator, and show_node_id have been renamed to unsafe-reset-all, show-validator, and show-node-id FEATURES * [lcd] Can now query governance proposals by ProposalStatus @@ -39,7 +48,7 @@ FEATURES * Modules can test random combinations of their own operations * Applications can integrate operations and invariants from modules together for an integrated simulation * [baseapp] Initialize validator set on ResponseInitChain -* [cosmos-sdk-cli] Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd +* [cosmos-sdk-cli] Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd * This allows SDK users to initialize a new project repository. * [tests] Remotenet commands for AWS (awsnet) * [networks] Added ansible scripts to upgrade seed nodes on a network @@ -47,6 +56,7 @@ FEATURES * [gov] Add slashing for validators who do not vote on a proposal * [cli] added `gov query-proposals` command to CLI. Can filter by `depositer`, `voter`, and `status` * [core] added BaseApp.Seal - ability to seal baseapp parameters once they've been set +* [scripts] added log output monitoring to DataDog using Ansible scripts * [gov] added TallyResult type that gets added stored in Proposal after tallying is finished IMPROVEMENTS @@ -54,7 +64,7 @@ IMPROVEMENTS * [cli] Improve error messages for all txs when the account doesn't exist * [tools] Remove `rm -rf vendor/` from `make get_vendor_deps` * [x/auth] Recover ErrorOutOfGas panic in order to set sdk.Result attributes correctly -* [x/stake] Add revoked to human-readable validator +* [x/stake] Add revoked to human-readable validator * [spec] \#967 Inflation and distribution specs drastically improved * [tests] Add tests to example apps in docs * [x/gov] Votes on a proposal can now be queried @@ -62,10 +72,15 @@ IMPROVEMENTS * [tests] Fixes ansible scripts to work with AWS too * [tests] \#1806 CLI tests are now behind the build flag 'cli_test', so go test works on a new repo * [x/gov] Initial governance parameters can now be set in the genesis file -* [x/stake] \#1815 Sped up the processing of `EditValidator` txs. +* [x/stake] \#1815 Sped up the processing of `EditValidator` txs. +* [server] \#1930 Transactions indexer indexes all tags by default. +* [x/stake] \#2000 Added tests for new staking endpoints +* [x/stake] [#2023](https://github.com/cosmos/cosmos-sdk/pull/2023) Terminate iteration loop in `UpdateBondedValidators` and `UpdateBondedValidatorsFull` when the first revoked validator is encountered and perform a sanity check. +* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present. * [spec] Added simple piggy bank distribution spec BUG FIXES +* \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988) * \#1666 Add intra-tx counter to the genesis validators * \#1797 Fix off-by-one error in slashing for downtime * \#1787 Fixed bug where Tally fails due to revoked/unbonding validator @@ -74,16 +89,17 @@ BUG FIXES * \#1799 Fix `gaiad export` * \#1828 Force user to specify amount on create-validator command by removing default * \#1839 Fixed bug where intra-tx counter wasn't set correctly for genesis validators -* [tests] \#1675 Fix non-deterministic `test_cover` +* [staking] [#1858](https://github.com/cosmos/cosmos-sdk/pull/1858) Fixed bug where the cliff validator was not be updated correctly +* [tests] \#1675 Fix non-deterministic `test_cover` * [client] \#1551: Refactored `CoreContext` * Renamed `CoreContext` to `QueryContext` * Removed all tx related fields and logic (building & signing) to separate structure `TxContext` in `x/auth/client/context` * Cleaned up documentation and API of what used to be `CoreContext` * Implemented `KeyType` enum for key info - -BUG FIXES * \#1666 Add intra-tx counter to the genesis validators * [tests] \#1551: Fixed invalid LCD test JSON payload in `doIBCTransfer` * \#1787 Fixed bug where Tally fails due to revoked/unbonding validator +* \#1787 Fixed bug where Tally fails due to revoked/unbonding validator * [basecoin] Fixes coin transaction failure and account query [discussion](https://forum.cosmos.network/t/unmarshalbinarybare-expected-to-read-prefix-bytes-75fbfab8-since-it-is-registered-concrete-but-got-0a141dfa/664/6) +* [cli] \#1997 Handle panics gracefully when `gaiacli stake {delegation,unbond}` fail to unmarshal delegation. diff --git a/README.md b/README.md index cf8e7d3b8..75761f5f4 100644 --- a/README.md +++ b/README.md @@ -12,37 +12,33 @@ [![riot.im](https://img.shields.io/badge/riot.im-JOIN%20CHAT-green.svg)](https://riot.im/app/#/room/#cosmos-sdk:matrix.org) The Cosmos-SDK is a framework for building blockchain applications in Golang. -It is being used to build `Gaia`, the first implementation of the [Cosmos Hub](https://cosmos.network), +It is being used to build `Gaia`, the first implementation of the [Cosmos Hub](https://cosmos.network/docs/), **WARNING**: The SDK has mostly stabilized, but we are still making some breaking changes. -**Note**: The `master` branch is an active development branch. For the latest -release, see the [release page](https://github.com/cosmos/cosmos-sdk/releases). - **Note**: Requires [Go 1.10+](https://golang.org/dl/) ## Gaia Testnet -For more information on connecting to the testnet, see -[cmd/gaia/testnets](/cmd/gaia/testnets) +To join the latest testnet, follow +[the guide](https://cosmos.network/docs/getting-started/full-node.html#setting-up-a-new-node). -For the latest status of the testnet, see the [status -file](/cmd/gaia/testnets/STATUS.md). +For status updates and genesis files, see the +[testnets repo](https://github.com/cosmos/testnets). ## Install -See the [install instructions](/docs/install.md) +See the +[install instructions](https://cosmos.network/docs/getting-started/installation.html). ## Quick Start -- [Documentation](https://cosmos.network/docs/) -- [Examples](/examples) -- [Cosmos Hub Specification](https://cosmos.network/docs/spec/) - +See the [Cosmos Docs](https://cosmos.network/docs/) + +- [Getting started with the SDK](https://cosmos.network/docs/sdk/core/intro.html) +- [SDK Examples](/examples) +- [Join the testnet](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node) ## Disambiguation diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 03a374bcd..d7398d899 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -387,7 +387,8 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg } // set the signed validators for addition to context in deliverTx - app.signedValidators = req.Validators + // TODO: communicate this result to the address to pubkey map in slashing + app.signedValidators = req.LastCommitInfo.GetValidators() return } @@ -412,11 +413,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { Log: result.Log, GasWanted: result.GasWanted, GasUsed: result.GasUsed, - Fee: cmn.KI64Pair{ - []byte(result.FeeDenom), - result.FeeAmount, - }, - Tags: result.Tags, + Tags: result.Tags, } } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index b8909e226..db584834a 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -6,6 +6,7 @@ import ( "net/http" "regexp" "testing" + "time" "github.com/cosmos/cosmos-sdk/client/tx" @@ -375,73 +376,76 @@ func TestValidatorQuery(t *testing.T) { require.Equal(t, 1, len(pks)) validator1Owner := sdk.AccAddress(pks[0].Address()) - validator := getValidator(t, port, validator1Owner) - bech32ValAddress, err := sdk.Bech32ifyValPub(pks[0]) - require.NoError(t, err) - assert.Equal(t, validator.PubKey, bech32ValAddress, "The returned validator does not hold the correct data") + assert.Equal(t, validator.Owner, validator1Owner, "The returned validator does not hold the correct data") } func TestBonding(t *testing.T) { name, password, denom := "test", "1234567890", "steak" - addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + addr, seed := CreateAddr(t, name, password, GetKeyBase(t)) cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() validator1Owner := sdk.AccAddress(pks[0].Address()) + validator := getValidator(t, port, validator1Owner) - // create bond TX - resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner) + resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner, 60) tests.WaitForHeight(resultTx.Height+1, port) - // check if tx was committed require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - // query sender acc := getAccount(t, port, addr) coins := acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf(denom).Int64()) - // query validator bond := getDelegation(t, port, addr, validator1Owner) require.Equal(t, "60.0000000000", bond.Shares) + summary := getDelegationSummary(t, port, addr) + + require.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations") + require.Equal(t, "60.0000000000", summary.Delegations[0].Shares) + require.Len(t, summary.UnbondingDelegations, 0, "Delegation summary holds all unbonding-delegations") + + bondedValidators := getDelegatorValidators(t, port, addr) + require.Len(t, bondedValidators, 1) + require.Equal(t, validator1Owner, bondedValidators[0].Owner) + require.Equal(t, validator.DelegatorShares.Add(sdk.NewDec(60)).String(), bondedValidators[0].DelegatorShares.String()) + + bondedValidator := getDelegatorValidator(t, port, addr, validator1Owner) + require.Equal(t, validator1Owner, bondedValidator.Owner) + ////////////////////// // testing unbonding - // create unbond TX - resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner) + resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner, 60) tests.WaitForHeight(resultTx.Height+1, port) - // query validator - bond = getDelegation(t, port, addr, validator1Owner) - require.Equal(t, "30.0000000000", bond.Shares) - - // check if tx was committed require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - // should the sender should have not received any coins as the unbonding has only just begun - // query sender + // sender should have not received any coins as the unbonding has only just begun acc = getAccount(t, port, addr) coins = acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) - // query unbonding delegation - validatorAddr := sdk.AccAddress(pks[0].Address()) - unbondings := getUndelegations(t, port, addr, validatorAddr) - assert.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") - assert.Equal(t, "30", unbondings[0].Balance.Amount.String()) + unbondings := getUndelegations(t, port, addr, validator1Owner) + require.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") + require.Equal(t, "60", unbondings[0].Balance.Amount.String()) - // query summary - summary := getDelegationSummary(t, port, addr) + summary = getDelegationSummary(t, port, addr) - assert.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations") - assert.Equal(t, "30.0000000000", summary.Delegations[0].Shares) - assert.Len(t, summary.UnbondingDelegations, 1, "Delegation summary holds all unbonding-delegations") - assert.Equal(t, "30", summary.UnbondingDelegations[0].Balance.Amount.String()) + require.Len(t, summary.Delegations, 0, "Delegation summary holds all delegations") + require.Len(t, summary.UnbondingDelegations, 1, "Delegation summary holds all unbonding-delegations") + require.Equal(t, "60", summary.UnbondingDelegations[0].Balance.Amount.String()) + + bondedValidators = getDelegatorValidators(t, port, addr) + require.Len(t, bondedValidators, 0, "There's no delegation as the user withdraw all funds") + + // TODO Undonding status not currently implemented + // require.Equal(t, sdk.Unbonding, bondedValidators[0].Status) // TODO add redelegation, need more complex capabilities such to mock context and // TODO check summary for redelegation @@ -564,7 +568,7 @@ func TestUnrevoke(t *testing.T) { signingInfo := getSigningInfo(t, port, pkString) tests.WaitForHeight(4, port) require.Equal(t, true, signingInfo.IndexOffset > 0) - require.Equal(t, int64(0), signingInfo.JailedUntil) + require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil) require.Equal(t, true, signingInfo.SignedBlocksCounter > 0) } @@ -756,64 +760,89 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. // ============= Stake Module ================ func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) rest.DelegationWithoutRat { - - // get the account to get the sequence res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var bond rest.DelegationWithoutRat + err := cdc.UnmarshalJSON([]byte(body), &bond) require.Nil(t, err) + return bond } func getUndelegations(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) []stake.UnbondingDelegation { - - // get the account to get the sequence res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var unbondings []stake.UnbondingDelegation + err := cdc.UnmarshalJSON([]byte(body), &unbondings) require.Nil(t, err) + return unbondings } func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary { - - // get the account to get the sequence res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var summary rest.DelegationSummary + err := cdc.UnmarshalJSON([]byte(body), &summary) require.Nil(t, err) + return summary } func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { - - // get the account to get the sequence var res *http.Response var body string + if len(query) > 0 { res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil) } else { res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil) } require.Equal(t, http.StatusOK, res.StatusCode, body) + var txs []tx.Info + err := cdc.UnmarshalJSON([]byte(body), &txs) require.Nil(t, err) + return txs } -func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence +func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.BechValidator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var bondedValidators []stake.BechValidator + + err := cdc.UnmarshalJSON([]byte(body), &bondedValidators) + require.Nil(t, err) + + return bondedValidators +} + +func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) stake.BechValidator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var bondedValidator stake.BechValidator + err := cdc.UnmarshalJSON([]byte(body), &bondedValidator) + require.Nil(t, err) + + return bondedValidator +} + +func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.AccAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { acc := getAccount(t, port, delegatorAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() - chainID := viper.GetString(client.FlagChainID) - // send jsonStr := []byte(fmt.Sprintf(`{ "name": "%s", "password": "%s", @@ -825,14 +854,15 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, { "delegator_addr": "%s", "validator_addr": "%s", - "delegation": { "denom": "%s", "amount": "60" } + "delegation": { "denom": "%s", "amount": "%d" } } ], "begin_unbondings": [], "complete_unbondings": [], "begin_redelegates": [], "complete_redelegates": [] - }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, "steak")) + }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, "steak", amount)) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -844,16 +874,13 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, } func doBeginUnbonding(t *testing.T, port, seed, name, password string, - delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { + delegatorAddr, validatorAddr sdk.AccAddress, amount int64) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence acc := getAccount(t, port, delegatorAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() - chainID := viper.GetString(client.FlagChainID) - // send jsonStr := []byte(fmt.Sprintf(`{ "name": "%s", "password": "%s", @@ -866,13 +893,14 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, { "delegator_addr": "%s", "validator_addr": "%s", - "shares": "30" + "shares": "%d" } ], "complete_unbondings": [], "begin_redelegates": [], "complete_redelegates": [] - }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr)) + }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, amount)) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -886,14 +914,12 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, func doBeginRedelegation(t *testing.T, port, seed, name, password string, delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence acc := getAccount(t, port, delegatorAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - // send jsonStr := []byte(fmt.Sprintf(`{ "name": "%s", "password": "%s", @@ -914,6 +940,7 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, ], "complete_redelegates": [] }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorSrcAddr, validatorDstAddr)) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -925,7 +952,6 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, } func getValidators(t *testing.T, port string) []stake.BechValidator { - // get the account to get the sequence res, body := Request(t, port, "GET", "/stake/validators", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var validators []stake.BechValidator @@ -935,7 +961,6 @@ func getValidators(t *testing.T, port string) []stake.BechValidator { } func getValidator(t *testing.T, port string, validatorAddr sdk.AccAddress) stake.BechValidator { - // get the account to get the sequence res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var validator stake.BechValidator @@ -1033,7 +1058,7 @@ func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStat } func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence + acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() @@ -1067,7 +1092,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA } func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID int64) (resultTx ctypes.ResultBroadcastTxCommit) { - // get the account to get the sequence + acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 48de9598c..818eae1e8 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -173,7 +173,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 100)} acc := gapp.NewGenesisAccount(&accAuth) genesisState.Accounts = append(genesisState.Accounts, acc) - genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100)) + genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewDec(100)) } appState, err := wire.MarshalJSONIndent(cdc, genesisState) @@ -223,7 +223,7 @@ func startTM( proxy.NewLocalClientCreator(app), genDocProvider, dbProvider, - nm.DefaultMetricsProvider, + nm.DefaultMetricsProvider(tmcfg.Instrumentation), logger.With("module", "node"), ) if err != nil { diff --git a/client/tx/sign.go b/client/tx/sign.go index 75239ef7b..786c7fa0b 100644 --- a/client/tx/sign.go +++ b/client/tx/sign.go @@ -44,5 +44,5 @@ func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) { return } - w.Write(sig.Bytes()) + w.Write(sig) } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index bb4dc67f5..9b318c8b1 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -148,7 +148,8 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { tags := gov.EndBlocker(ctx, app.govKeeper) validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) - + // Add these new validators to the addr -> pubkey map. + app.slashingKeeper.AddValidators(ctx, validatorUpdates) return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, Tags: tags, @@ -181,6 +182,9 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // return sdk.ErrGenesisParse("").TraceCause(err, "") } + // load the address to pubkey map + slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) + gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState()) return abci.ResponseInitChain{ diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 1731fe2dc..e35b21c4b 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -185,7 +185,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState } acc := NewGenesisAccount(&accAuth) genaccs[i] = acc - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionsAcc)) // increase the supply + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionsAcc)) // increase the supply // add the validator if len(genTx.Name) > 0 { @@ -193,10 +193,10 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState validator := stake.NewValidator(genTx.Address, sdk.MustGetAccPubKeyBech32(genTx.PubKey), desc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionVal)) // increase the supply + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionVal)) // increase the supply // add some new shares to the validator - var issuedDelShares sdk.Rat + var issuedDelShares sdk.Dec validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, freeFermionVal) stakeData.Validators = append(stakeData.Validators, validator) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index f0bea1e17..6369b4ca6 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -48,7 +48,7 @@ func appStateFn(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage { // Default genesis state stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(1000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(1000) genesis := GenesisState{ Accounts: genesisAccounts, StakeData: stakeGenesis, diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 8d652bc43..244a9eb83 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -34,7 +34,7 @@ func init() { } func TestGaiaCLISend(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "") + tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) @@ -87,7 +87,7 @@ func TestGaiaCLISend(t *testing.T) { } func TestGaiaCLICreateValidator(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "") + tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) @@ -132,7 +132,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags)) require.Equal(t, validator.Owner, barAddr) - require.True(sdk.RatEq(t, sdk.NewRat(2), validator.Tokens)) + require.True(sdk.DecEq(t, sdk.NewDec(2), validator.Tokens)) // unbond a single share unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags) @@ -149,11 +149,11 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc) */ validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags)) - require.Equal(t, "1/1", validator.Tokens.String()) + require.Equal(t, "1.0000000000", validator.Tokens.String()) } func TestGaiaCLISubmitProposal(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe_reset_all", gaiadHome), "") + tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) diff --git a/cmd/gaia/cmd/gaiadebug/gaiadebug b/cmd/gaia/cmd/gaiadebug/gaiadebug deleted file mode 100755 index 642291fd3..000000000 Binary files a/cmd/gaia/cmd/gaiadebug/gaiadebug and /dev/null differ diff --git a/cmd/gaia/testnets/README.md b/cmd/gaia/testnets/README.md index ddfeef54c..e64db9f7d 100644 --- a/cmd/gaia/testnets/README.md +++ b/cmd/gaia/testnets/README.md @@ -1,498 +1,7 @@ -# Connect to the `gaia-7001` Testnet +# DEPRECATED -_**NOTE:**_ We are aware this documentation is a work in progress. We are actively -working to improve the tooling and the documentation to make this process as painless as -possible. In the meantime, join the [Validator Chat](https://riot.im/app/#/room/#cosmos_validators:matrix.org) -for technical support, and [open issues](https://github.com/cosmos/cosmos-sdk) if you run into any! Thanks very much for your patience and support. :) +The content of this file was moved to the `/docs` folder and is hosted on the +[website](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node). -## Setting Up a New Node - -These instructions are for setting up a brand new full node from scratch. If you ran a full node on a previous testnet you will need to start from scratch due to some breaking changes in key format. - -### Install Go - -Install `go` by following the [official docs](https://golang.org/doc/install). -**Go 1.10+** is required for the Cosmos SDK. Remember to properly setup your `$GOPATH`, `$GOBIN`, and `$PATH` variables, for example: - -```bash -mkdir -p $HOME/go/bin -echo "export GOPATH=$HOME/go" >> ~/.bash_profile -echo "export GOBIN=$GOPATH/bin" >> ~/.bash_profile -echo "export PATH=$PATH:$GOBIN" >> ~/.bash_profile -``` - -### Install Cosmos SDK - -Next, let's install the testnet's version of the Cosmos SDK. - -```bash -mkdir -p $GOPATH/src/github.com/cosmos -cd $GOPATH/src/github.com/cosmos -git clone https://github.com/cosmos/cosmos-sdk -cd cosmos-sdk && git checkout v0.22.0 -make get_tools && make get_vendor_deps && make install -``` - -That will install the `gaiad` and `gaiacli` binaries. Verify that everything is OK: - -```bash -$ gaiad version -0.22.0 - -$ gaiacli version -0.22.0 -``` - -### Node Setup - -Create the required configuration files, and initialize the node: - -```bash -gaiad init --name -``` - -> *NOTE:* Note that only ASCII characters are supported for the `--name`. Using Unicode renders your node unreachable. - -You can also edit this `moniker` in the `~/.gaiad/config/config.toml` file: - -```toml -# A custom human readable name for this node -moniker = "" -``` - -Your full node has been initialized! - -## Upgrading From Previous Testnet - -These instructions are for full nodes that have ran on previous testnets and -would like to upgrade to the latest testnet. - -### Reset Data - -First, remove the outdated files and reset the data. - -```bash -rm $HOME/.gaiad/config/addrbook.json $HOME/.gaiad/config/genesis.json -gaiad unsafe_reset_all -``` - -Your node is now in a pristine state while keeping the original `priv_validator.json` and `config.toml`. -If you had any sentry nodes or full nodes setup before, your node will still try to connect to them, -but may fail if they haven't also been upgraded. - -**WARNING:** Make sure that every node has a unique `priv_validator.json`. -Do not copy the `priv_validator.json` from an old node to multiple new nodes. -Running two nodes with the same `priv_validator.json` will cause you to double sign. - -NOTE: key formats changed between gaia-6002 and gaia-7000. If you're trying to upgrade from gaia-6002, -you will also need to delete your `priv_validator.json`: - -``` -rm $HOME/.gaiad/config/priv_validator.json -``` - -### Software Upgrade - -Now it is time to upgrade the software: - -```bash -cd $GOPATH/src/github.com/cosmos/cosmos-sdk -git fetch --all && git checkout v0.22.0 -make update_tools && make get_vendor_deps && make install -``` - -Your full node has been cleanly upgraded! - -## Genesis & Seeds - -### Copy the Genesis File - -Fetch the testnet's `genesis.json` file and place it in `gaiad`'s config directory. - -```bash -mkdir -p $HOME/.gaiad/config -curl https://gist.githubusercontent.com/cwgoes/311da6ba05be6e113185a716538a44c3/raw/7b6e784cf29761b5781488006313bd69d164aa6c/chris-final.json > $HOME/.gaiad/config/genesis.json -``` - -### Add Seed Nodes - -Your node needs to know how to find peers. You'll need to add healthy seed nodes to `$HOME/.gaiad/config/config.toml`. Here are some seed nodes you can use: - -```toml -# Comma separated list of seed nodes to connect to -seeds = "718145d422a823fd2a4e1e36e91b92bb0c4ddf8e@gaia-7000.coinculture.net:26656,5922bf29b48a18c2300b85cc53f424fce23927ab@67.207.73.206:26656,7c8b8fd03577cd4817f5be1f03d506f879df98d8@gaia-7000-seed1.interblock.io:26656,a28737ff02391a6e00a1d3b79befd57e68e8264c@gaia-7000-seed2.interblock.io:26656,987ffd26640cd03d08ed7e53b24dfaa7956e612d@gaia-7000-seed3.interblock.io:26656" -``` - -If those seeds aren't working, you can find more seeds and persistent peers on the [Cosmos Explorer](https://explorecosmos.network/nodes). Open the the `Full Nodes` pane and select nodes that do not have private (`10.x.x.x`) or [local IP addresses](https://en.wikipedia.org/wiki/Private_network). The `Persistent Peer` field contains the connection string. For best results use 4-6. - -For more information on seeds and peers, [read this](https://github.com/tendermint/tendermint/blob/develop/docs/using-tendermint.md#peers). - -## Run a Full Node - -Start the full node with this command: - -```bash -gaiad start -``` - -Check that everything is running smoothly: - -```bash -gaiacli status -``` - -View the status of the network with the [Cosmos Explorer](https://explorecosmos.network). Once your full node syncs up to the current block height, you should see it appear on the [list of full nodes](https://explorecosmos.network/validators). If it doesn't show up, that's ok--the Explorer does not connect to every node. - -## Generating Keys - -### A Note on Keys in Cosmos: - -There are three types of key representations that are used in this tutorial: - -- `cosmosaccaddr` - * Derived from account keys generated by `gaiacli keys add` - * Used to receive funds - * e.g. `cosmosaccaddr15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc` - -- `cosmosaccpub` - * Derived from account keys generated by `gaiacli keys add` - * e.g. `cosmosaccpub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm` - -- `cosmosvalpub` - * Generated when the node is created with `gaiad init`. - * Get this value with `gaiad tendermint show_validator` - * e.g. `cosmosvalpub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c` - -### Key Generation - -You'll need an account private and public key pair \(a.k.a. `sk, pk` respectively\) to be able to receive funds, send txs, bond tx, etc. - -To generate a new key \(default _ed25519_ elliptic curve\): - -```bash -gaiacli keys add -``` - -Next, you will have to create a passphrase to protect the key on disk. The output of the above command will contain a _seed phrase_. Save the _seed phrase_ in a safe place in case you forget the password! - -If you check your private keys, you'll now see ``: - -```bash -gaiacli keys show -``` - -You can see all your available keys by typing: - -```bash -gaiacli keys list -``` - -View the validator pubkey for your node by typing: - -```bash -gaiad tendermint show_validator -``` - -**WARNING:** We strongly recommend NOT using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds. This is not as important on the testnets, but is good security practice and should be followed. - -## Fund your account - -The best way to get tokens is from the [Cosmos Testnet Faucet](https://faucetcosmos.network). If the faucet is not working for you, try asking [#cosmos-validators](https://riot.im/app/#/room/#cosmos-validators:matrix.org). The faucet needs the `cosmosaccaddr` from the account you wish to use for staking. - -After receiving tokens to your address, you can view your account's balance by typing: - -```bash -gaiacli account -``` - -> _*Note:*_ When you query an account balance with zero tokens, you will get this error: `No account with address was found in the state.` This can also happen if you fund the account before your node has fully synced with the chain. These are both normal. Also, we're working on improving our error messages! - -## Run a Validator Node - -[Validators](https://cosmos.network/validators) are responsible for committing new blocks to the blockchain through voting. A validator's stake is slashed if they become unavailable, double sign a transaction, or don't cast their votes. If you only want to run a full node, a VM in the cloud is fine. However, if you are want to become a validator for the Hub's `mainnet`, you should research hardened setups. Please read [Sentry Node Architecture](https://forum.cosmos.network/t/sentry-node-architecture-overview/454) to protect your node from DDOS and ensure high-availability. Also see the [technical requirements](https://github.com/cosmos/cosmos/blob/master/VALIDATORS_FAQ.md#technical-requirements)). There's also more info on our [website](https://cosmos.network/validators). - -### Create Your Validator - -Your `cosmosvalpub` can be used to create a new validator by staking tokens. You can find your validator pubkey by running: - -```bash -gaiad tendermint show_validator -``` - -Next, craft your `gaiacli stake create-validator` command: - -> _*NOTE:*_ Don't use more `steak` thank you have! You can always get more by using the [Faucet](https://faucetcosmos.network/)! - -```bash -gaiacli stake create-validator \ - --amount=5steak \ - --pubkey=$(gaiad tendermint show_validator) \ - --address-validator= - --moniker="choose a moniker" \ - --chain-id=gaia-7001 \ - --from= -``` - -### Edit Validator Description - -You can edit your validator's public description. This info is to identify your validator, and will be relied on by delegators to decide which validators to stake to. Make sure to provide input for every flag below, otherwise the field will default to empty (`--moniker` defaults to the machine name). - -The `--identity` can be used as to verify identity with systems like Keybase or UPort. When using with Keybase `--identity` should be populated with a 16-digit string that is generated with a [keybase.io](https://keybase.io) account. It's a cryptographically secure method of verifying your identity across multiple online networks. The Keybase API allows us to retrieve your Keybase avatar. This is how you can add a logo to your validator profile. - -```bash -gaiacli stake edit-validator - --address-validator= - --moniker="choose a moniker" \ - --website="https://cosmos.network" \ - --identity=6A0D65E29A4CBC8E - --details="To infinity and beyond!" - --chain-id=gaia-7001 \ - --from= -``` - -### View Validator Description -View the validator's information with this command: - -```bash -gaiacli stake validator \ - --address-validator= \ - --chain-id=gaia-7001 -``` - -Your validator is active if the following command returns anything: - -```bash -gaiacli advanced tendermint validator-set | grep "$(gaiad tendermint show_validator)" -``` - -You should also be able to see your validator on the [Explorer](https://explorecosmos.network/validators). You are looking for the `bech32` encoded `address` in the `~/.gaiad/config/priv_validator.json` file. - -> _*Note:*_ To be in the validator set, you need to have more total voting power than the 100th validator. This is not normally an issue. - -### Problem #1: My validator has `voting_power: 0` - -Your validator has become auto-unbonded. In `gaia-7001`, we unbond validators if they do not vote on `50` of the last `100` blocks. Since blocks are proposed every ~2 seconds, a validator unresponsive for ~100 seconds will become unbonded. This usually happens when your `gaiad` process crashes. - -Here's how you can return the voting power back to your validator. First, if `gaiad` is not running, start it up again: - -```bash -gaiad start -``` - -Wait for your full node to catch up to the latest block. Next, run the following command. Note that `` is the address of your validator account, and `` is the name of the validator account. You can find this info by running `gaiacli keys list`. - -```bash -gaiacli stake unrevoke --chain-id=gaia-7001 --from= -``` - -**WARNING:** If you don't wait for `gaiad` to sync before running `unrevoke`, you will receive an error message telling you your validator is still jailed. - -Lastly, check your validator again to see if your voting power is back. - -```bash -gaiacli status -``` - -You may notice that your voting power is less than it used to be. That's because you got slashed for downtime! - -### Problem #2: My `gaiad` crashes because of `too many open files` - -The default number of files Linux can open (per-process) is `1024`. `gaiad` is known to open more than `1024` files. This causes the process to crash. A quick fix is to run `ulimit -n 4096` (increase the number of open files allowed) and then restart the process with `gaiad start`. If you are using `systemd` or another process manager to launch `gaiad` this may require some configuration at that level. A sample `systemd` file to fix this issue is below: - -```toml -# /etc/systemd/system/gaiad.service -[Unit] -Description=Cosmos Gaia Node -After=network.target - -[Service] -Type=simple -User=ubuntu -WorkingDirectory=/home/ubuntu -ExecStart=/home/ubuntu/go/bin/gaiad start -Restart=on-failure -RestartSec=3 -LimitNOFILE=4096 - -[Install] -WantedBy=multi-user.target -``` - -## Delegating to a Validator - -On the upcoming mainnet, you can delegate `Atom` to a validator. These [delegators](https://cosmos.network/resources/delegators) can receive part of the validator's fee revenue. Read more about the [Cosmos Token Model](https://github.com/cosmos/cosmos/raw/master/Cosmos_Token_Model.pdf). - -### Bond Tokens - -On the testnet, we delegate `steak` instead of `Atom`. Here's how you can bond tokens to a testnet validator: - -```bash -gaiacli stake delegate \ - --amount=10steak \ - --address-delegator= \ - --address-validator= \ - --from= \ - --chain-id=gaia-7001 -``` - -While tokens are bonded, they are pooled with all the other bonded tokens in the network. Validators and delegators obtain a percentage of shares that equal their stake in this pool. - -> _*NOTE:*_ Don't use more `steak` thank you have! You can always get more by using the [Faucet](https://gaia.faucetcosmos.network/)! - -### Unbond Tokens - -If for any reason the validator misbehaves, or you want to unbond a certain amount of tokens, use this following command. You can unbond a specific amount of`shares`\(eg:`12.1`\) or all of them \(`MAX`\). - -```bash -gaiacli stake unbond \ - --address-delegator= \ - --address-validator= \ - --shares=MAX \ - --from= \ - --chain-id=gaia-7001 -``` - -You can check your balance and your stake delegation to see that the unbonding went through successfully. - -```bash -gaiacli account - -gaiacli stake delegation \ - --address-delegator= \ - --address-validator= \ - --chain-id=gaia-7001 -``` - -## Governance - -Governance is the process from which users in the Cosmos Hub can come to consensus on software upgrades, parameters of the mainnet or on custom text proposals. This is done through voting on proposals, which will be submitted by `Atom` holders on the mainnet. - -Some considerations about the voting process: - -- Voting is done by bonded `Atom` holders on a 1 bonded `Atom` 1 vote basis -- Delegators inherit the vote of their validator if they don't vote -- **Validators MUST vote on every proposal**. If a validator does not vote on a proposal, they will be **partially slashed** -- Votes are tallied at the end of the voting period (2 weeks on mainnet). Each address can vote multiple times to update its `Option` value (paying the transaction fee each time), only the last casted vote will count as valid -- Voters can choose between options `Yes`, `No`, `NoWithVeto` and `Abstain` -At the end of the voting period, a proposal is accepted if `(YesVotes/(YesVotes+NoVotes+NoWithVetoVotes))>1/2` and `(NoWithVetoVotes/(YesVotes+NoVotes+NoWithVetoVotes))<1/3`. It is rejected otherwise - -For more information about the governance process and how it works, please check out the Governance module [specification](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/governance). - -### Create a Governance proposal - -In order to create a governance proposal, you must submit an initial deposit along with the proposal details: - -- `title`: Title of the proposal -- `description`: Description of the proposal -- `type`: Type of proposal. Must be of value _Text_ (types _SoftwareUpgrade_ and _ParameterChange_ not supported yet). - -```bash -gaiacli gov submit-proposal \ - --title= \ - --description=<description> \ - --type=<Text/ParameterChange/SoftwareUpgrade> \ - --proposer=<account_cosmosaccaddr> \ - --deposit=<40steak> \ - --from=<name> \ - --chain-id=gaia-7001 -``` - - -### Increase deposit - -In order for a proposal to be broadcasted to the network, the amount deposited must be above a `minDeposit` value (default: `10 steak`). If the proposal you previously created didn't meet this requirement, you can still increase the total amount deposited to activate it. Once the minimum deposit is reached, the proposal enters voting period: - -```bash -gaiacli gov deposit \ - --proposalID=<proposal_id> \ - --depositer=<account_cosmosaccaddr> \ - --deposit=<200steak> \ - --from=<name> \ - --chain-id=gaia-7001 -``` - -> _NOTE_: Proposals that don't meet this requirement will be deleted after `MaxDepositPeriod` is reached. - -#### Query proposal - -Once created, you can now query information of the proposal: - -```bash -gaiacli gov query-proposal \ - --proposalID=<proposal_id> \ - --chain-id=gaia-7001 -``` - -### Vote on a proposal - -After a proposal's deposit reaches the `MinDeposit` value, the voting period opens. Bonded `Atom` holders can then cast vote on it: - -```bash -gaiacli gov vote \ - --proposalID=<proposal_id> \ - --voter=<account_cosmosaccaddr> \ - --option=<Yes/No/NoWithVeto/Abstain> \ - --from=<name> \ - --chain-id=gaia-7001 -``` - -#### Query vote - -Check the vote with the option you just submitted: - -```bash -gaiacli gov query-vote \ - --proposalID=<proposal_id> \ - --voter=<account_cosmosaccaddr> \ - --chain-id=gaia-7001 -``` - -## Other Operations - -### Send Tokens - -```bash -gaiacli send \ - --amount=10faucetToken \ - --chain-id=gaia-7001 \ - --from=<key_name> \ - --to=<destination_cosmosaccaddr> -``` - -> _*NOTE:*_ The `--amount` flag accepts the format `--amount=<value|coin_name>`. - -Now, view the updated balances of the origin and destination accounts: - -```bash -gaiacli account <account_cosmosaccaddr> -gaiacli account <destination_cosmosaccaddr> -``` - -You can also check your balance at a given block by using the `--block` flag: - -```bash -gaiacli account <account_cosmosaccaddr> --block=<block_height> -``` - -## Create your Own Testnet - -To create your own testnet, first each validator will need to install gaiad and -run `gen-tx`: - -```bash -gaiad init gen-tx --name <account_name> -``` - -The validator will be prompted to enter a password for their new account. - -This populates `$HOME/.gaiad/gen-tx/` with a json file. - -Now these json files need to be aggregated together via Github, a Google form, pastebin or other methods. - -Place all files on one computer in `$HOME/.gaiad/gen-tx/` - -```bash -gaiad init --gen-txs -o --chain=<chain-name> -``` - -This will generate a `genesis.json` in `$HOME/.gaiad/config/genesis.json` distribute this file to all validators on your testnet. \ No newline at end of file +The rest of this folder was moved to the [testnets +repo](https://github.com/cosmos/testnets). diff --git a/cmd/gaia/testnets/STATUS.md b/cmd/gaia/testnets/STATUS.md index 442de16ec..b972eb802 100644 --- a/cmd/gaia/testnets/STATUS.md +++ b/cmd/gaia/testnets/STATUS.md @@ -1,4 +1,6 @@ -# TESTNET STATUS +# DEPRECATED + +See [testnets repo](https://github.com/cosmos/testnets). ## *July 22, 2018, 5:30 EST* - Gaia-7001 Consensus Failure diff --git a/crypto/encode_test.go b/crypto/encode_test.go index a2b5b1aea..9e7097855 100644 --- a/crypto/encode_test.go +++ b/crypto/encode_test.go @@ -15,12 +15,14 @@ type byter interface { Bytes() []byte } -func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) { +func checkAminoBinary(t *testing.T, src, dst interface{}, size int) { // Marshal to binary bytes. bz, err := cdc.MarshalBinaryBare(src) require.Nil(t, err, "%+v", err) - // Make sure this is compatible with current (Bytes()) encoding. - require.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch") + if byterSrc, ok := src.(byter); ok { + // Make sure this is compatible with current (Bytes()) encoding. + require.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch") + } // Make sure we have the expected length. if size != -1 { require.Equal(t, size, len(bz), "Amino binary size mismatch") @@ -55,8 +57,6 @@ func ExamplePrintRegisteredTypes() { //| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | //| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | //| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | - //| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | - //| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | | } func TestKeyEncodings(t *testing.T) { @@ -86,13 +86,11 @@ func TestKeyEncodings(t *testing.T) { require.EqualValues(t, tc.privKey, priv3) // Check (de/en)codings of Signatures. - var sig1, sig2, sig3 tcrypto.Signature + var sig1, sig2 []byte sig1, err := tc.privKey.Sign([]byte("something")) require.NoError(t, err) checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways. require.EqualValues(t, sig1, sig2) - checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes. - require.EqualValues(t, sig1, sig3) // Check (de/en)codings of PubKeys. pubKey := tc.privKey.PubKey() @@ -107,7 +105,7 @@ func TestKeyEncodings(t *testing.T) { func TestNilEncodings(t *testing.T) { // Check nil Signature. - var a, b tcrypto.Signature + var a, b []byte checkAminoJSON(t, &a, &b, true) require.EqualValues(t, a, b) diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index aa1c0cdeb..ae036362e 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -199,7 +199,7 @@ func (kb dbKeybase) Get(name string) (Info, error) { // Sign signs the msg with the named key. // It returns an error if the key doesn't exist or the decryption fails. -func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig tmcrypto.Signature, pub tmcrypto.PubKey, err error) { +func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { info, err := kb.Get(name) if err != nil { return diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 114c0e7d5..652a36bcb 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -146,7 +146,7 @@ func TestSignVerify(t *testing.T) { cases := []struct { key crypto.PubKey data []byte - sig crypto.Signature + sig []byte valid bool }{ // proper matches diff --git a/crypto/keys/types.go b/crypto/keys/types.go index 62ff8bd16..a68626489 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -16,7 +16,7 @@ type Keybase interface { Delete(name, passphrase string) error // Sign some bytes, looking up the private key to use - Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) + Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) // CreateMnemonic creates a new mnemonic, and derives a hierarchical deterministic // key from that. diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger_secp256k1.go index f82115a3d..8cb175d4f 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger_secp256k1.go @@ -114,7 +114,7 @@ func (pkl PrivKeyLedgerSecp256k1) Equals(other tmcrypto.PrivKey) bool { // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning // an error, so this should only trigger if the private key is held in memory // for a while before use. -func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) (tmcrypto.Signature, error) { +func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) ([]byte, error) { sig, err := pkl.signLedgerSecp256k1(msg) if err != nil { return nil, err @@ -135,13 +135,8 @@ func (pkl PrivKeyLedgerSecp256k1) getPubKey() (key tmcrypto.PubKey, err error) { return key, err } -func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) (tmcrypto.Signature, error) { - sigBytes, err := pkl.ledger.SignSECP256K1(pkl.Path, msg) - if err != nil { - return nil, err - } - - return tmsecp256k1.SignatureSecp256k1FromBytes(sigBytes), nil +func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) ([]byte, error) { + return pkl.ledger.SignSECP256K1(pkl.Path, msg) } func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey, err error) { diff --git a/docs/PRIORITIES.md b/docs/PRIORITIES.md index fc7a62892..d628c136e 100644 --- a/docs/PRIORITIES.md +++ b/docs/PRIORITIES.md @@ -1,8 +1,8 @@ - ## Fees - Collection - Gas price based on parameter - (which gets changed automatically) + - https://github.com/cosmos/cosmos-sdk/issues/1921 - Per block gas usage as % - Windowing function - Block N, @@ -22,7 +22,8 @@ - Only use text proposals - On-chain mechanism for agreeing on when to "flip" to new functionality -## Slashing/Stability +## Staking/Slashing/Stability +- Unbonding state for validators https://github.com/cosmos/cosmos-sdk/issues/1676 - current: downtime, double signing during unbonding - who gets slashed when -- needs review about edge cases - need to communicate to everyone that lite has this edge case @@ -68,3 +69,10 @@ ## Slashing/Stability - tendermint evidence: we don’t yet slash byzantine signatures (signing at all) when not bonded. +# Other priority + +## gaiad // gaiacli +- Documentation // language + +## gaialite +- Documentation // language diff --git a/docs/clients/lcd-rest-api.yaml b/docs/clients/lcd-rest-api.yaml index 063e3a55b..3b8349f4b 100644 --- a/docs/clients/lcd-rest-api.yaml +++ b/docs/clients/lcd-rest-api.yaml @@ -493,6 +493,49 @@ paths: 500: description: Internal Server Error + /stake/delegators/{delegatorAddr}/validators: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + get: + summary: Query all validators that a delegator is bonded to + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + + /stake/delegators/{delegatorAddr}/validators/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + get: + summary: Query a validator that a delegator is bonded to + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + /stake/delegators/{delegatorAddr}/txs: parameters: - in: path diff --git a/docs/getting-started/full-node.md b/docs/getting-started/full-node.md index 82583a3c9..87c28581b 100644 --- a/docs/getting-started/full-node.md +++ b/docs/getting-started/full-node.md @@ -1,5 +1,11 @@ # Join the Testnet +::: tip Current Testnet +See the [testnet repo](https://github.com/cosmos/testnets) for +information on the latest testnet, including the correct version +of the Cosmos-SDK to use and details about the genesis file. +::: + Please ensure you have the [Cosmos SDK](/getting-started/installation.md) installed. If you ran a full node on a previous testnet, please skip to [Upgrading From Previous Testnet](#upgrading-from-previous-testnet). ## Setting Up a New Node @@ -35,7 +41,7 @@ First, remove the outdated files and reset the data. ```bash rm $HOME/.gaiad/config/addrbook.json $HOME/.gaiad/config/genesis.json -gaiad unsafe_reset_all +gaiad unsafe-reset-all ``` Your node is now in a pristine state while keeping the original `priv_validator.json` and `config.toml`. If you had any sentry nodes or full nodes setup before, @@ -52,30 +58,40 @@ Now it is time to upgrade the software: ```bash cd $GOPATH/src/github.com/cosmos/cosmos-sdk -git fetch --all && git checkout v0.19.0 +git fetch --all && git checkout master make update_tools && make get_vendor_deps && make install ``` +Note we use `master` here since it contains the latest stable release. +See the [testnet repo](https://github.com/cosmos/testnets) +for details on which version is needed for which testnet, +and the [SDK release page](https://github.com/cosmos/cosmos-sdk/releases) +for details on each release. + Your full node has been cleanly upgraded! ## Genesis & Seeds ### Copy the Genesis File -Copy the testnet's `genesis.json` file and place it in `gaiad`'s config directory. +Fetch the testnet's `genesis.json` file into `gaiad`'s config directory. ```bash mkdir -p $HOME/.gaiad/config -cp -a $GOPATH/src/github.com/cosmos/cosmos-sdk/cmd/gaia/testnets/gaia-6002/genesis.json $HOME/.gaiad/config/genesis.json +curl https://raw.githubusercontent.com/cosmos/testnets/master/latest/genesis.json > $HOME/.gaiad/config/genesis.json ``` +Note we use the `latest` directory in the [testnets repo](https://github.com/cosmos/testnets) +which contains details for the latest testnet. If you are connecting to a different testnet, ensure you +get the right files. + ### Add Seed Nodes Your node needs to know how to find peers. You'll need to add healthy seed nodes to `$HOME/.gaiad/config/config.toml`. Here are some seed nodes you can use: ```toml # Comma separated list of seed nodes to connect to -seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6002.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656" +seeds = "718145d422a823fd2a4e1e36e91b92bb0c4ddf8e@gaia-testnet.coinculture.net:26656,5922bf29b48a18c2300b85cc53f424fce23927ab@67.207.73.206:26656,7c8b8fd03577cd4817f5be1f03d506f879df98d8@gaia-7000-seed1.interblock.io:26656,a28737ff02391a6e00a1d3b79befd57e68e8264c@gaia-7000-seed2.interblock.io:26656,987ffd26640cd03d08ed7e53b24dfaa7956e612d@gaia-7000-seed3.interblock.io:26656" ``` If those seeds aren't working, you can find more seeds and persistent peers on the [Cosmos Explorer](https://explorecosmos.network/nodes). Open the the `Full Nodes` pane and select nodes that do not have private (`10.x.x.x`) or [local IP addresses](https://en.wikipedia.org/wiki/Private_network). The `Persistent Peer` field contains the connection string. For best results use 4-6. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 70922dc20..c23f3b02c 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -20,12 +20,18 @@ echo "export PATH=$PATH:$GOBIN" >> ~/.bash_profile ## Install Cosmos SDK Next, let's install the testnet's version of the Cosmos SDK. +You can find information about the latest testnet and the right +version of the Cosmos-SDK for it in the [testnets +repo](https://github.com/cosmos/testnets#testnet-status). +Here we'll use the `master` branch, which contains the latest stable release. +If necessary, make sure you `git checkout` the correct +[released version](https://github.com/cosmos/cosmos-sdk/releases). ```bash mkdir -p $GOPATH/src/github.com/cosmos cd $GOPATH/src/github.com/cosmos git clone https://github.com/cosmos/cosmos-sdk -cd cosmos-sdk && git checkout v0.19.0 +cd cosmos-sdk && git checkout master make get_tools && make get_vendor_deps && make install ``` @@ -33,10 +39,7 @@ That will install the `gaiad` and `gaiacli` binaries. Verify that everything is ```bash $ gaiad version -0.19.0-c6711810 - $ gaiacli version -0.19.0-c6711810 ``` ## Run a Full Node diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index bafdfc4bd..000000000 --- a/docs/install.md +++ /dev/null @@ -1,59 +0,0 @@ -# Install - -The fastest and easiest way to install the Cosmos SDK binaries -is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_ubuntu.sh) on a fresh Ubuntu instance. Similarly, you can run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_bsd.sh) on a fresh FreeBSD instance. Read the scripts before running them to ensure no untrusted connection is being made, for example we're making curl requests to download golang. Also read the comments / instructions carefully (i.e., reset your terminal after running the script). - -Cosmos SDK can be installed to -`$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program: - -``` -go get github.com/cosmos/cosmos-sdk -``` - -If the dependencies have been updated with breaking changes, or if -another branch is required, `dep` is used for dependency management. -Thus, assuming you've already run `go get` or otherwise cloned the repo, -the correct way to install is: - -``` -cd $GOPATH/src/github.com/cosmos/cosmos-sdk -make get_tools -make get_vendor_deps -make install -make install_examples -``` - -This will install `gaiad` and `gaiacli` and four example binaries: -`basecoind`, `basecli`, `democoind`, and `democli`. - -Verify that everything is OK by running: - -``` -gaiad version -``` - -you should see: - -``` -0.17.3-a5a78eb -``` - -then with: - -``` -gaiacli version -``` -you should see the same version (or a later one for both). - -## Update - -Get latest code (you can also `git fetch` only the version desired), -ensure the dependencies are up to date, then recompile. - -``` -cd $GOPATH/src/github.com/cosmos/cosmos-sdk -git fetch -a origin -git checkout VERSION -make get_vendor_deps -make install -``` diff --git a/docs/introduction/tendermint-cosmos.md b/docs/introduction/tendermint-cosmos.md new file mode 100644 index 000000000..f48106a1d --- /dev/null +++ b/docs/introduction/tendermint-cosmos.md @@ -0,0 +1,27 @@ +## Tendermint and Cosmos + +Blockchains can be divided into three conceptual layers: + +- **Networking:** Responsible for propagating transactions. +- **Consensus:** Enables validator nodes to agree on the next set of transactions to process (i.e. add blocks of transactions to the blockchain). +- **Application:** Responsible for updating the state given a set of transactions, i.e. processing transactions. + +The *networking* layer makes sure that each node receives transactions. The *consensus* layer makes sure that each node agrees on the same transactions to modify their local state. As for the *application* layer, it processes transactions. Given a transaction and a state, the application will return a new state. In Bitcoin for example, the application state is a ledger or list of balances for each account (in reality, it's a list of UTXO, short for Unspent Transaction Output, but let's call them balances for the sake of simplicity), and the transactions modify the application's state by changing these list of balances. In the case of Ethereum, the application is a virtual machine. Each transaction goes through this virtual machine and modifies the application state according to the the smart contract that is called within it. + +Before Tendermint, building a blockchain required building all three layers from the ground up. It was such a tedious task that most developers preferred to fork or replicate the Bitcoin codebase, but were constrainted by the limitations of Bitcoin's protocol. The Ethereum Virtual Machine (EVM) was designed to solve this problem and simplify decentralized application development by allowing customizable logic to be executed through smart contracts. But it did not resolve the limitations (interoperability, scalability and customization) of blockchains themselves. Go-Ethereum remains a very monolithic tech stack that is difficult to hard-fork much like Bitcoin's codebase. + +Tendermint was designed to address these issues and provide developers with an laternative. The goal of Tendermint is to provide the *networking* and *consensus* layers of a blockchain as a generic engine to power any application developers want to build. With Tendermint, developers only have to focus on the *application* layer, thereby saving hundreds of hours of work and costly development set-ups. For reference, Tendermint also designates the name of the byzantine fault tolerant consensus algorithm used within the Tendermint Core engine. + +Tendermint connects the blockchain engine, Tendermint Core (*networking* and *consensus* layers), to the *application* layer via a socket protocol called the [ABCI](https://github.com/tendermint/abci), short for Application-BlockChain Interface. Developers only have to implement a few messages to build an ABCI-enabled application that runs on top of the Tendermint Core engine. ABCI is language agnostic, meaning that developers can build the *application* part of their blockchain in any programming language. Building on top of the Tendermint Core engine also provides the following benefits: + +- **Public or private blockchain capable.** Developers can deploy any blockchain application, permissioned (private) and permissionless (public), on top of Tendermint Core. +- **Performance.** Tendermint Core is a state-of-the-art blockchain consensus engine able to handle large number of transactions in short timespan. A block time on Tendermint Core can be as low as one second and can process thousands of transactions in that time period. +- **Instant finality.** A property of the Tendermint consensus algorithm is instant finality, meaning that forks are never created, as long as less than a third of the validators are malicious (byzantine). Users can be sure their transactions are finalized as soon as a block is created. +- **Security.** Tendermint Core's consensus is not only fault tolerant, it’s optimally Byzantine fault-tolerant (BFT), with accountability. If the blockchain forks, there is a way to determine liability. +- **Light-client support**. Tendermint provides built-in light-clients. + +But most importantly, Tendermint is natively compatible with the [Inter-Blockchain Communication Protocol](https://github.com/cosmos/cosmos-sdk/tree/develop/docs/spec/ibc) (IBC). This means that any Tendermint-based blockchain, whether public or private, can be natively connected to the Cosmos ecosystem and securely exchange tokens with other blockchains in the ecosystem. Note that benefiting from interoperability via IBC and Cosmos preserves the sovereignty of your Tendermint chain. Non-Tendermint chains can also be connected to Cosmos via IBC adapters or Peg-Zones, but this is out of scope for this document. + +For a more detailed overview of the Cosmos ecosystem, you can read [this article](https://blog.cosmos.network/understanding-the-value-proposition-of-cosmos-ecaef63350d). + +For more on Tendermint, go [here](tendermint.md) \ No newline at end of file diff --git a/docs/introduction/tendermint.md b/docs/introduction/tendermint.md index 07ce67618..62db8cdbc 100644 --- a/docs/introduction/tendermint.md +++ b/docs/introduction/tendermint.md @@ -5,9 +5,43 @@ Tendermint is software for securely and consistently replicating an application Tendermint is designed to be easy-to-use, simple-to-understand, highly performant, and useful for a wide variety of distributed applications. ## Byzantine Fault Tolerance + The ability to tolerate machines failing in arbitrary ways, including becoming malicious, is known as Byzantine fault tolerance (BFT). The theory of BFT is decades old, but software implementations have only became popular recently, due largely to the success of “blockchain technology” like Bitcoin and Ethereum. Blockchain technology is just a re-formalization of BFT in a more modern setting, with emphasis on peer-to-peer networking and cryptographic authentication. The name derives from the way transactions are batched in blocks, where each block contains a cryptographic hash of the previous one, forming a chain. In practice, the blockchain data structure actually optimizes BFT design. ## Application Blockchain Interface + Tendermint consists of two chief technical components: a blockchain consensus engine and a generic application interface. The consensus engine, called Tendermint Core, ensures that the same transactions are recorded on every machine in the same order. The application interface, called the Application Blockchain Interface (ABCI), enables the transactions to be processed in any programming language. Unlike other blockchain and consensus solutions developers can use Tendermint for BFT state machine replication in any programming language or development environment. Visit the [Tendermint docs](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html#abci-overview) for a deep dive into the ABCI. -The [Cosmos SDK](/sdk/overview.md) is an ABCI framework written in Go. [Lotion JS](/lotion/overview.md) is an ABCI framework written in JavaScript. +## Understanding the roles of the different layers + +It is important to have a good understanding of the respective responsibilities of both the *Application* and the *Consensus Engine*. + +Responsibilities of the *Consensus Engine*: +- Propagate transactions +- Agree on the order of valid transactions + +Reponsibilities of the *Application*: +- Generate Transactions +- Check if transactions are valid +- Process Transactions (includes state changes) + +It is worth underlining that the *Consensus Engine* has knowledge of a given validator set for each block, but that it is the responsiblity of the *Application* to trigger validator set changes. This is the reason why it is possible to build both **public and private chains** with the Cosmos-SDK and Tendermint. A chain will be public or private depending on the rules, defined at application level, that governs a validator's set changes. + +The ABCI establishes the connection between the *Consensus Engine* and the *Application*. Essentially, it boils down to two messages: + +- `CheckTx`: Ask the application if the transaction is valid. When a validator's node receives a transaction, it will run `CheckTx` on it. If the transaction is valid, it is added to the mempool. +- `DeliverTx`: Ask the application to process the transaction and update the state. + +Let us give a high-level overview of how the *Consensus Engine* and the *Application* interract with each other. + +- At all times, when the consensus engine (Tendermint Core) of a validator node receives a transaction, it passes it to the application via `CheckTx` to check its validity. If it is valid, the transaction is added to the mempool. +- Let us say we are at block N. There is a validator set V. A proposer of the next block is selected from V by the *Consensus Engine*. The proposer gathers valid transaction from its mempool to form a new block. Then, the block is gossiped to other validators to be signed/commited. The block becomes block N+1 once 2/3+ of V have signed a *precommit* on it (For a more detailed explanation of the consensus algorithm, click [here](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm)). +- When block N+1 is signed by 2/3+ of V, it is gossipped to full-nodes. When full-nodes receive the block, they confirm its validity. A block is valid if it it holds valid signatures from more than 2/3 of V and if all the transactions in the block are valid. To check the validity of transactions, the *Consensus Engine* transfers them to the application via `DeliverTx`. After each transaction, `DeliverTx` returns a new state if the transaction was valid. At the end of the block, a final state is committed. Of course, this means that the order of transaction within a block matters. + +## Application frameworks + +Even if Tendermint makes it easy for developers to build their own blockchain by enabling them to focus on the *Application* layer of their blockchain, building an *Application* can be a challenging task in itself. This is why *Application Frameworks* exist. They provide developers with a secure and features-heavy environment to develop Tendermint-based applications. Here are some examples of *Application Frameworks* : + +- The [Cosmos SDK](/sdk/overview.md) is an ABCI framework written in Go. +- [Lotion JS](/lotion/overview.md) is an ABCI framework written in JavaScript. + diff --git a/docs/introduction/what-is-cosmos.md b/docs/introduction/what-is-cosmos.md index 2e20c329c..f9470d7c9 100644 --- a/docs/introduction/what-is-cosmos.md +++ b/docs/introduction/what-is-cosmos.md @@ -4,4 +4,4 @@ Cosmos is a decentralized network of independent parallel blockchains, each powe The first blockchain in the Cosmos Network is the [Cosmos Hub](), whose native token is the Atom. Cosmos is a permission-less network, meaning that anybody can build a blockchain on it. -Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary. +Cosmos can interoperate with multiple other applications and cryptocurrencies. By creating a new zone, you can plug any blockchain system into the Cosmos hub and pass tokens back and forth between those zones, without the need for an intermediary. \ No newline at end of file diff --git a/docs/light/api.md b/docs/light/api.md index 41abed5da..d4e66eaf1 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -479,6 +479,62 @@ Returns on error: } ``` +### /stake/delegators/{delegatorAddr}/validators - GET + +url: /stake/delegators/{delegatorAddr}/validators + +Functionality: Query all validators that a delegator is bonded to. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{} +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"TODO", + "result":{} +} +``` + +### /stake/delegators/{delegatorAddr}/validators/{validatorAddr} - GET + +url: /stake/delegators/{delegatorAddr}/validators/{validatorAddr} + +Functionality: Query a validator that a delegator is bonded to + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{} +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"TODO", + "result":{} +} +``` + ### /stake/delegators/{delegatorAddr}/txs - GET url: /stake/delegators/{delegatorAddr}/txs diff --git a/docs/light/specification.md b/docs/light/specification.md index d8897b244..15f36b014 100644 --- a/docs/light/specification.md +++ b/docs/light/specification.md @@ -323,6 +323,14 @@ return KeyOutput{ TODO +### [/stake/delegators/{delegatorAddr}/validators](api.md#stakedelegatorsdelegatorAddrvalidators---get) + +TODO + +### [/stake/delegators/{delegatorAddr}/validators/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrvalidatorsvalidatorAddr---get) + +TODO + ### [/stake/delegators/{delegatorAddr}/txs](api.md#stakedelegatorsdelegatorAddrtxs---get) TODO diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 0767745e5..5d02548a2 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -10,7 +10,6 @@ 🚧 We are actively working on improving documentation for Gaiacli and Gaiad. ::: - `gaiacli` is the command line interface to manage accounts and transactions on Cosmos testnets. Here is a list of useful `gaiacli` commands, including usage examples. ### Key Types @@ -18,18 +17,20 @@ There are three types of key representations that are used: - `cosmosaccaddr` - * Derived from account keys generated by `gaiacli keys add` - * Used to receive funds - * e.g. `cosmosaccaddr15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc` + + - Derived from account keys generated by `gaiacli keys add` + - Used to receive funds + - e.g. `cosmosaccaddr15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc` - `cosmosaccpub` - * Derived from account keys generated by `gaiacli keys add` - * e.g. `cosmosaccpub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm` + + - Derived from account keys generated by `gaiacli keys add` + - e.g. `cosmosaccpub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm` - `cosmosvalpub` - * Generated when the node is created with `gaiad init`. - * Get this value with `gaiad tendermint show_validator` - * e.g. `cosmosvalpub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c` + - Generated when the node is created with `gaiad init`. + - Get this value with `gaiad tendermint show-validator` + - e.g. `cosmosvalpub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c` ### Generate Keys @@ -58,11 +59,11 @@ gaiacli keys list View the validator pubkey for your node by typing: ```bash -gaiad tendermint show_validator +gaiad tendermint show-validator ``` ::: danger Warning -We strongly recommend *NOT* using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds. +We strongly recommend _NOT_ using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds. ::: ### Get Tokens @@ -86,7 +87,7 @@ We're working on improving our error messages! ```bash gaiacli send \ --amount=10faucetToken \ - --chain-id=gaia-6002 \ + --chain-id=gaia-7005 \ --name=<key_name> \ --to=<destination_cosmosaccaddr> ``` @@ -119,15 +120,15 @@ On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond t ```bash gaiacli stake delegate \ --amount=10steak \ - --address-validator=$(gaiad tendermint show_validator) \ - --from=<key_name> \ - --chain-id=gaia-6002 + --address-validator=$(gaiad tendermint show-validator) \ + --name=<key_name> \ + --chain-id=gaia-7005 ``` While tokens are bonded, they are pooled with all the other bonded tokens in the network. Validators and delegators obtain a percentage of shares that equal their stake in this pool. ::: tip Note - Don't use more `steak` thank you have! You can always get more by using the [Faucet](https://faucetcosmos.network/)! +Don't use more `steak` thank you have! You can always get more by using the [Faucet](https://faucetcosmos.network/)! ::: ### Unbond Tokens @@ -136,10 +137,10 @@ If for any reason the validator misbehaves, or you want to unbond a certain amou ```bash gaiacli stake unbond begin \ - --address-validator=$(gaiad tendermint show_validator) \ - --shares-percent=1 \ - --from=<key_name> \ - --chain-id=gaia-6002 + --address-validator=$(gaiad tendermint show-validator) \ + --shares=MAX \ + --name=<key_name> \ + --chain-id=gaia-7005 ``` Later you must use the `gaiacli stake unbond complete` command to finish @@ -151,8 +152,8 @@ gaiacli account <account_cosmosaccaddr> gaiacli stake delegation \ --address-delegator=<account_cosmosaccaddr> \ - --address-validator=$(gaiad tendermint show_validator) \ - --chain-id=gaia-6002 + --address-validator=$(gaiad tendermint show-validator) \ + --chain-id=gaia-7005 ``` ## Light Client Daemon diff --git a/docs/sdk/core/app2.md b/docs/sdk/core/app2.md index 3de7f7b8d..9a689cca6 100644 --- a/docs/sdk/core/app2.md +++ b/docs/sdk/core/app2.md @@ -150,7 +150,7 @@ func NewCodec() *wire.Codec { ``` Note: We also register the types in the `tendermint/tendermint/crypto` module so that `crypto.PubKey` -and `crypto.Signature` are encoded/decoded correctly. +is encoded/decoded correctly. Amino supports encoding and decoding in both a binary and JSON format. See the [codec API docs](https://godoc.org/github.com/tendermint/go-amino#Codec) for more details. @@ -166,7 +166,7 @@ type app2Tx struct { sdk.Msg PubKey crypto.PubKey - Signature crypto.Signature + Signature []byte } // This tx only has one Msg. diff --git a/docs/sdk/core/app3.md b/docs/sdk/core/app3.md index 66bb05521..203f61e44 100644 --- a/docs/sdk/core/app3.md +++ b/docs/sdk/core/app3.md @@ -160,7 +160,7 @@ The standard form for signatures is `StdSignature`: // the first transaction made by the account. type StdSignature struct { crypto.PubKey `json:"pub_key"` // optional - crypto.Signature `json:"signature"` + []byte `json:"signature"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` } diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index 764a89356..3c7f71f6d 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -183,7 +183,7 @@ type app2Tx struct { sdk.Msg PubKey crypto.PubKey - Signature crypto.Signature + Signature []byte } // This tx only has one Msg. @@ -191,7 +191,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg { return []sdk.Msg{tx.Msg} } -func (tx app2Tx) GetSignature() crypto.Signature { +func (tx app2Tx) GetSignature() []byte { return tx.Signature } diff --git a/docs/sdk/overview.md b/docs/sdk/overview.md index 40d1e0bb5..905982f62 100644 --- a/docs/sdk/overview.md +++ b/docs/sdk/overview.md @@ -1,14 +1,16 @@ # Cosmos SDK Overview -The Cosmos-SDK is a framework for building Tendermint ABCI applications in -Golang. It is designed to allow developers to easily create custom interoperable -blockchain applications within the Cosmos Network. +The [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk) is a framework for building multi-asset Proof-of-Stake (PoS) blockchains, like the Cosmos Hub, as well as Proof-Of-Authority (PoA) blockchains. -To achieve its goals of flexibility and security, the SDK makes extensive use of -the [object-capability -model](https://en.wikipedia.org/wiki/Object-capability_model) -and the [principle of least -privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). +The goal of the Cosmos-SDK is to allow developers to easily create custom interoperable blockchain applications within the Cosmos Network without having to recreate common blockchain functionality, thus removing the complexity of building a Tendermint ABCI application. We envision the SDK as the npm-like framework to build secure blockchain applications on top of Tendermint. + +In terms of its design, the SDK optimizes flexibility and security. The framework is designed around a modular execution stack which allows applications to mix and match elements as desired. In addition, all modules are sandboxed for greater application security. + +It is based on two major principles: + +- **Composability:** Anyone can create a module for the Cosmos-SDK and integrating the already-built modules is as simple as importing them into your blockchain application. + +- **Capabilities:** The SDK is inspired by capabilities-based security, and informed by years of wrestling with blockchain state-machines. Most developers will need to access other 3rd party modules when building their own modules. Given that the Cosmos-SDK is an open framework and that we assume that some of those modules may be malicious, we designed the SDK using object-capabilities (ocaps) based principles. In practice, this means that instead of having each module keep an access control list for other modules, each module implements special objects called keepers that can be passed to other modules to grant a pre-defined set of capabilities. For example, if an instance of module A's keepers is passed to module B, the latter will be able to call a restricted set of module A's functions. The capabilities of each keeper are defined by the module's developer, and it's the developer's job to understand and audit the safety of foreign code from 3rd party modules based on the capabilities they are passing into each 3rd party module. For a deeper look at capabilities, you can read this [article](http://habitatchronicles.com/2017/05/what-are-capabilities/). For an introduction to object-capabilities, see this [article](http://habitatchronicles.com/2017/05/what-are-capabilities/). diff --git a/docs/sdk/sdk-by-examples/intro.md b/docs/sdk/sdk-by-examples/intro.md new file mode 100644 index 000000000..6404cead6 --- /dev/null +++ b/docs/sdk/sdk-by-examples/intro.md @@ -0,0 +1,7 @@ +**SDK by Examples** offers an alternative and complementary way to learn about the Cosmos-SDK. It contains several examples that showcase how to build a decentralised application on top of the Cosmos-SDK from start to finish. + +Without further ado, let us get into it! + +- [Simple governance example](./simple-governance/intro.md) + +If you have an example you would like to add to the list, feel free to open a PR [here](https://github.com/cosmos/cosmos-sdk/pulls). diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-cli.md b/docs/sdk/sdk-by-examples/simple-governance/app-cli.md new file mode 100644 index 000000000..52590c62f --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-cli.md @@ -0,0 +1,23 @@ +## Application CLI + +**File: [`cmd/simplegovcli/maing.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovcli/main.go)** + +To interact with our application, let us add the commands from the `simple_governance` module to our `simpleGov` application, as well as the pre-built SDK commands: + +```go +// cmd/simplegovcli/main.go +... + rootCmd.AddCommand( + client.GetCommands( + simplegovcmd.GetCmdQueryProposal("proposals", cdc), + simplegovcmd.GetCmdQueryProposals("proposals", cdc), + simplegovcmd.GetCmdQueryProposalVotes("proposals", cdc), + simplegovcmd.GetCmdQueryProposalVote("proposals", cdc), + )...) + rootCmd.AddCommand( + client.PostCommands( + simplegovcmd.PostCmdPropose(cdc), + simplegovcmd.PostCmdVote(cdc), + )...) +... +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-codec.md b/docs/sdk/sdk-by-examples/simple-governance/app-codec.md new file mode 100644 index 000000000..9d35b4363 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-codec.md @@ -0,0 +1,21 @@ +## Application codec + +**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** + +Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules. + +```go +func MakeCodec() *wire.Codec { + var cdc = wire.NewCodec() + wire.RegisterCrypto(cdc) // Register crypto. + sdk.RegisterWire(cdc) // Register Msgs + bank.RegisterWire(cdc) + simplestake.RegisterWire(cdc) + simpleGov.RegisterWire(cdc) + + // Register AppAccount + cdc.RegisterInterface((*auth.Account)(nil), nil) + cdc.RegisterConcrete(&types.AppAccount{}, "simpleGov/Account", nil) + return cdc +} +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-commands.md b/docs/sdk/sdk-by-examples/simple-governance/app-commands.md new file mode 100644 index 000000000..4eb2f9221 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-commands.md @@ -0,0 +1,9 @@ +## App commands + +We will need to add the newly created commands to our application. To do so, go to the `cmd` folder inside your root directory: + +```bash +// At root level of directory +cd cmd +``` +`simplegovd` is the folder that stores the command for running the server daemon, whereas `simplegovcli` defines the commands of your application. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md b/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md new file mode 100644 index 000000000..84858edf4 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-constructor.md @@ -0,0 +1,61 @@ +## Application constructor + +**File: [`app/app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go)** + +Now, we need to define the constructor for our application. + +```go +func NewSimpleGovApp(logger log.Logger, db dbm.DB) *SimpleGovApp +``` + +In this function, we will: + +- Create the codec + +```go +var cdc = MakeCodec() +``` + +- Instantiate our application. This includes creating the keys to access each of the substores. + +```go +// Create your application object. + var app = &SimpleGovApp{ + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + cdc: cdc, + capKeyMainStore: sdk.NewKVStoreKey("main"), + capKeyAccountStore: sdk.NewKVStoreKey("acc"), + capKeyStakingStore: sdk.NewKVStoreKey("stake"), + capKeySimpleGovStore: sdk.NewKVStoreKey("simpleGov"), + } +``` + +- Instantiate the keepers. Note that keepers generally need access to other module's keepers. In this case, make sure you only pass an instance of the keeper for the functionality that is needed. If a keeper only needs to read in another module's store, a read-only keeper should be passed to it. + +```go +app.coinKeeper = bank.NewKeeper(app.accountMapper) +app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper,app.RegisterCodespace(simplestake.DefaultCodespace)) +app.simpleGovKeeper = simpleGov.NewKeeper(app.capKeySimpleGovStore, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(simpleGov.DefaultCodespace)) +``` + +- Declare the handlers. + +```go +app.Router(). + AddRoute("bank", bank.NewHandler(app.coinKeeper)). + AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)). + AddRoute("simpleGov", simpleGov.NewHandler(app.simpleGovKeeper)) +``` + +- Initialize the application. + +```go +// Initialize BaseApp. + app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeySimpleGovStore, app.capKeyStakingStore) + app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) + err := app.LoadLatestVersion(app.capKeyMainStore) + if err != nil { + cmn.Exit(err.Error()) + } + return app +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-design.md b/docs/sdk/sdk-by-examples/simple-governance/app-design.md new file mode 100644 index 000000000..fcc050979 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-design.md @@ -0,0 +1,78 @@ +## Application design + +### Application description + +For this tutorial, we will code a **simple governance application**, accompagnied by a **simple governance module**. It will allow us to explain most of the basic notions required to build a functioning application on the Cosmos-SDK. Note that this is not the governance module used for the Cosmos Hub. A much more [advanced governance module](https://github.com/cosmos/cosmos-sdk/tree/develop/x/gov) will be used instead. + +All the code for the `simple_governance` application can be found [here](https://github.com/gamarin2/cosmos-sdk/tree/module_tutorial/examples/simpleGov/x/simple_governance). You'll notice that the module and app aren't located at the root level of the repo but in the examples directory. This is just for convenience, you can code your module and application directly in the root directory. + +Without further talk, let's get into it! + +### Requirements + +We will start by writting down your module's requirements. We are designing a simple governance module, in which we want: + +- Simple text proposals, that any coin holder can submit. +- Proposals must be submitted with a deposit in Atoms. If the deposit is larger than a `MinDeposit`, the associated proposal enters the voting period. Otherwise it is rejected. +- Bonded Atom holders can vote on proposal on a 1 bonded Atom 1 vote basis +- Bonded Atom holders can choose between 3 options when casting a vote: `Yes`, `No` and `Abstain`. +- If, at the end of the voting period, there are more `Yes` votes than `No` votes, the proposal is accepted. Otherwise, it is rejected. +- Voting period is 2 weeks + +When designing a module, it is good to adopt a certain methodology. Remember that a blockchain application is just a replicated state-machine. The state is the representation of the application at a given time. It is up to the application developer to define what the state represents, depending on the goal of the application. For example, the state of a simple cryptocurrency application will be a mapping of addresses to balances. + +The state can be updated according to predefined rules. Given a state and a transaction, the state-machine (i.e. the application) will return a new state. In a blockchain application, transactions are bundled in blocks, but the logic is the same. Given a state and a set of transactions (a block), the application returns a new state. A SDK-module is just a subset of the application, but it is based on the same principles. As a result, module developers only have to define a subset of the state and a subset of the transaction types, which trigger state transitions. + +In summary, we have to define: + +- A `State`, which represents a subset of the current state of the application. +- `Transactions`, which contain messages that trigger state transitions. + +### State + +Here, we will define the types we need (excluding transaction types), as well as the stores in the multistore. + +Our voting module is very simple, we only need a single type: `Proposal`. `Proposals` are item to be voted upon. They can be submitted by any user. A deposit has to be provided. + +```go +type Proposal struct { + Title string // Title of the proposal + Description string // Description of the proposal + Submitter sdk.Address // Address of the submitter. Needed to refund deposit if proposal is accepted. + SubmitBlock int64 // Block at which proposal is submitted. Also the block at which voting period begins. + State string // State can be either "Open", "Accepted" or "Rejected" + + YesVotes int64 // Total number of Yes votes + NoVotes int64 // Total number of No votes + AbstainVotes int64 // Total number of Abstain votes +} +``` + +In terms of store, we will just create one [KVStore](#kvstore) in the multistore to store `Proposals`. We will also store the `Vote` (`Yes`, `No` or `Abstain`) chosen by each voter on each proposal. + + +### Messages + +As a module developer, what you have to define are not `Transactions`, but `Messages`. Both transactions and messages exist in the Cosmos-SDK, but a transaction differs from a message in that a message is contained in a transaction. Transactions wrap around messages and add standard information like signatures and fees. As a module developer, you do not have to worry about transactions, only messages. + +Let us define the messages we need in order to modify the state. Based on the requirements above, we need to define two types of messages: + +- `SubmitProposalMsg`: to submit proposals +- `VoteMsg`: to vote on proposals + +```go +type SubmitProposalMsg struct { + Title string // Title of the proposal + Description string // Description of the proposal + Deposit sdk.Coins // Deposit paid by submitter. Must be > MinDeposit to enter voting period + Submitter sdk.Address // Address of the submitter +} +``` + +```go +type VoteMsg struct { + ProposalID int64 // ID of the proposal + Option string // Option chosen by voter + Voter sdk.Address // Address of the voter +} +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-init.md b/docs/sdk/sdk-by-examples/simple-governance/app-init.md new file mode 100644 index 000000000..a71be2bed --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-init.md @@ -0,0 +1,11 @@ +## Application initialization + +In the root of your fork of the SDK, create an `app` and `cmd` folder. In this folder, we will create the main file for our application, `app.go` and the repository to handle REST and CLI commands for our app. + +```bash +mkdir app cmd +mkdir -p cmd/simplegovcli cmd/simplegovd +touch app/app.go cmd/simplegovcli/main.go cmd/simplegovd/main.go +``` + +We will take care of these files later in the tutorial. The first step is to take care of our simple governance module. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md b/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md new file mode 100644 index 000000000..b7502d339 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-makefile.md @@ -0,0 +1,22 @@ +## Makefile + +The [Makefile](https://en.wikipedia.org/wiki/Makefile) compiles the Go program by defining a set of rules with targets and recipes. We'll need to add our application commands to it: + +``` +// Makefile +build_examples: +ifeq ($(OS),Windows_NT) + ... + go build $(BUILD_FLAGS) -o build/simplegovd.exe ./examples/simpleGov/cmd/simplegovd + go build $(BUILD_FLAGS) -o build/simplegovcli.exe ./examples/simpleGov/cmd/simplegovcli +else + ... + go build $(BUILD_FLAGS) -o build/simplegovd ./examples/simpleGov/cmd/simplegovd + go build $(BUILD_FLAGS) -o build/simplegovcli ./examples/simpleGov/cmd/simplegovcli +endif +... +install_examples: + ... + go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovd + go install $(BUILD_FLAGS) ./examples/simpleGov/cmd/simplegovcli +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-rest.md b/docs/sdk/sdk-by-examples/simple-governance/app-rest.md new file mode 100644 index 000000000..7e8469ad6 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-rest.md @@ -0,0 +1,57 @@ +##### Rest server + +**File: [`cmd/simplegovd/main.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/cmd/simplegovd/main.go)** + +The `simplegovd` command will run the daemon server as a background process. First, let us create some `utils` functions: + +```go +// cmd/simplegovd/main.go +// SimpleGovAppInit initial parameters +var SimpleGovAppInit = server.AppInit{ + AppGenState: SimpleGovAppGenState, + AppGenTx: server.SimpleAppGenTx, +} + +// SimpleGovAppGenState sets up the app_state and appends the simpleGov app state +func SimpleGovAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { + appState, err = server.SimpleAppGenState(cdc, appGenTxs) + if err != nil { + return + } + return +} + +func newApp(logger log.Logger, db dbm.DB) abci.Application { + return app.NewSimpleGovApp(logger, db) +} + +func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { + dapp := app.NewSimpleGovApp(logger, db) + return dapp.ExportAppStateJSON() +} +``` + +Now, let us define the command for the daemon server within the `main()` function: + +```go +// cmd/simplegovd/main.go +func main() { + cdc := app.MakeCodec() + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ + Use: "simplegovd", + Short: "Simple Governance Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + + server.AddCommands(ctx, cdc, rootCmd, SimpleGovAppInit, + server.ConstructAppCreator(newApp, "simplegov"), + server.ConstructAppExporter(exportAppState, "simplegov")) + + // prepare and add flags + rootDir := os.ExpandEnv("$HOME/.simplegovd") + executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) + executor.Execute() +} +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-structure.md b/docs/sdk/sdk-by-examples/simple-governance/app-structure.md new file mode 100644 index 000000000..c213b673b --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/app-structure.md @@ -0,0 +1,55 @@ +## Application structure + +Now, that we have built all the pieces we need, it is time to integrate them into the application. Let us exit the `/x` director go back at the root of the SDK directory. + + +```bash +// At root level of directory +cd app +``` + +We are ready to create our simple governance application! + +*Note: You can check the full file (with comments!) [here](link)* + +The `app.go` file is the main file that defines your application. In it, you will declare all the modules you need, their keepers, handlers, stores, etc. Let us take a look at each section of this file to see how the application is constructed. + +Secondly, we need to define the name of our application. + +```go +const ( + appName = "SimpleGovApp" +) +``` + +Then, let us define the structure of our application. + +```go +// Extended ABCI application +type SimpleGovApp struct { + *bam.BaseApp + cdc *wire.Codec + + // keys to access the substores + capKeyMainStore *sdk.KVStoreKey + capKeyAccountStore *sdk.KVStoreKey + capKeyStakingStore *sdk.KVStoreKey + capKeySimpleGovStore *sdk.KVStoreKey + + // keepers + feeCollectionKeeper auth.FeeCollectionKeeper + coinKeeper bank.Keeper + stakeKeeper simplestake.Keeper + simpleGovKeeper simpleGov.Keeper + + // Manage getting and setting accounts + accountMapper auth.AccountMapper +} +``` + +- Each application builds on top of the `BaseApp` template, hence the pointer. +- `cdc` is the codec used in our application. +- Then come the keys to the stores we need in our application. For our simple governance app, we need 3 stores + the main store. +- Then come the keepers and mappers. + +Let us do a quick reminder so that it is clear why we need these stores and keepers. Our application is primarily based on the `simple_governance` module. However, we have established in section [Keepers for our app](module-keeper.md) that our module needs access to two other modules: the `bank` module and the `stake` module. We also need the `auth` module for basic account functionalities. Finally, we need access to the main multistore to declare the stores of each of the module we use. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md b/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md new file mode 100644 index 000000000..f16f2b4e6 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/cast-vote.md @@ -0,0 +1,19 @@ +## Cast a vote to an existing proposal + +Let's cast a vote on the created proposal: + +```bash +simplegovcli vote --proposal-id=1 --option="No" +``` + +Get the value of the option from your casted vote : + +```bash +simplegovcli proposal-vote 1 <your_address> +``` + +You can also check all the casted votes of a proposal: + +```bash +simplegovcli proposals-votes 1 +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/intro.md b/docs/sdk/sdk-by-examples/simple-governance/intro.md new file mode 100644 index 000000000..ab4b5b6a5 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/intro.md @@ -0,0 +1,58 @@ +# SDK By Examples - Simple Governance Application + +In this tutorial, you will learn the basics of coding an application with the Cosmos-SDK. Applications built on top of the SDK are called *Application-specific blockchains*. They are decentralised applications running on their own blockchains. The application we will build in this tutorial is a simple governance application. + +Before getting in the bulk of the code, we will start by some introductory content on Tendermint, Cosmos and the programming philosophy of the SDK. Let us get started! + +## Table of contents: + +### Introduction - Prerequsite reading + +- [Intro to Tendermint and Cosmos](../../../introduction/tendermint-cosmos.md) +- [Tendermint Core and ABCI](../../../introduction/tendermint.md) +- [Intro to Cosmos-SDK](../../overview.md) +- [Starting your own project](start.md) + +### Setup and design phase + +- [Setup](setup.md) +- [Application design](app-design.md) + +### Implementation of the application + +**Important note: All the code for this application can be found [here](https://github.com/cosmos/cosmos-sdk/tree/fedekunze/module_tutorial/examples/simpleGov). Snippets will be provided throughout the tutorial, but please refer to the provided link for the full implementation details** + +- [Application initialization](app-init.md) +- Simple Governance module + + [Module initialization](module-init.md) + + [Types](module-types.md) + + [Keeper](module-keeper.md) + + [Handler](module-handler.md) + + [Wire](module-wire.md) + + [Errors](module-errors.md) + + Command-Line Interface and Rest API + * [Command-Line Interface](module-cli.md) + * [Rest API](module-rest.md) +- Bridging it all together + + [Application structure](app-structure.md) + + [Application CLI and Rest Server](app-commands.md) + * [Application CLI](app-cli.md) + * [Rest Server](app-rest.md) + + [Makefile](app-makefile.md) + + [Application constructor](app-constructor.md) + + [Application codec](app-codec.md) +- Running the application + + [Installation](run-install.md) + + [Submit a proposal](submit-proposal.md) + + [Cast a vote](cast-vote.md) + +## Useful links + +If you have any question regarding this tutorial or about development on the SDK, please reach out us through our official communication channels: + +- [Cosmos-SDK Riot Channel](https://riot.im/app/#/room/#cosmos-sdk:matrix.org) +- [Telegram](https://t.me/cosmosproject) + +Or open an issue on the SDK repo: + +- [Cosmos-SDK repo](https://github.com/cosmos/cosmos-sdk/) diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-cli.md b/docs/sdk/sdk-by-examples/simple-governance/module-cli.md new file mode 100644 index 000000000..b7e3143e3 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-cli.md @@ -0,0 +1,33 @@ +## Command-Line Interface (CLI) + +**File: [`x/simple_governance/client/cli/simple_governance.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/cli/simple_governance.go)** + +Go in the `cli` folder and create a `simple_governance.go` file. This is where we will define the commands for our module. + +The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the schema to build a command on top of Cobra: + +```go + // Declare flags + const( + Flag = "flag" + ... + ) + + // Main command function. One function for each command. + func Command(codec *wire.Codec) *cobra.Command { + // Create the command to return + command := &cobra.Command{ + Use: "actual command", + Short: "Short description", + Run: func(cmd *cobra.Command, args []string) error { + // Actual function to run when command is used + }, + } + + // Add flags to the command + command.Flags().<Type>(FlagNameConstant, <example_value>, "<Description>") + + return command + } +``` + diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-errors.md b/docs/sdk/sdk-by-examples/simple-governance/module-errors.md new file mode 100644 index 000000000..5597792a4 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-errors.md @@ -0,0 +1,7 @@ +## Errors + +**File: [`x/simple_governance/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/errors.go)** + +The `error.go` file allows us to define custom error messages for our module. Declaring errors should be relatively similar in all modules. You can look in the `error.go` file directly for a concrete example. The code is self-explanatory. + +Note that the errors of our module inherit from the `sdk.Error` interface and therefore possess the method `Result()`. This method is useful when there is an error in the `handler` and an error has to be returned in place of an actual result. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-handler.md b/docs/sdk/sdk-by-examples/simple-governance/module-handler.md new file mode 100644 index 000000000..fc142e0fa --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-handler.md @@ -0,0 +1,73 @@ +## Handler + +**File: [`x/simple_governance/handler.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/handler.go)** + +### Constructor and core handlers + +Handlers implement the core logic of the state-machine. When a transaction is routed from the app to the module, it is run by the `handler` function. + +In practice, one `handler` will be implemented for each message of the module. In our case, we have two message types. We will therefore need two `handler` functions. We will also need a constructor function to route the message to the correct `handler`: + +```go +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case SubmitProposalMsg: + return handleSubmitProposalMsg(ctx, k, msg) + case VoteMsg: + return handleVoteMsg(ctx, k, msg) + default: + errMsg := "Unrecognized gov Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} +``` + +The messages are routed to the appropriate `handler` depending on their type. For our simple governance module, we only have two `handlers`, that correspond to our two message types. They have similar signatures: + +```go +func handleSubmitProposalMsg(ctx sdk.Context, k Keeper, msg SubmitProposalMsg) sdk.Result +``` + +Let us take a look at the parameters of this function: + +- The context `ctx` to access the stores. +- The keeper `k` allows the handler to read and write from the different stores, including the module's store (`SimpleGovernance` in our case) and all the stores from other modules that the keeper `k` has been granted an access to (`stake` and `bank` in our case). +- The message `msg` that holds all the information provided by the sender of the transaction. + +The function returns a `Result` that is returned to the application. It contains several useful information such as the amount of `Gas` for this transaction and wether the message was succesfully processed or not. At this point, we exit the boundaries of our simple governance module and go back to root application level. The `Result` will differ from application to application. You can check the `sdk.Result` type directly [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/result.go) for more info. + +### BeginBlocker and EndBlocker + +In contrast to most smart-contracts platform, it is possible to perform automatic (i.e. not triggered by a transaction sent by an end-user) execution of logic in Cosmos-SDK applications. + +This automatic execution of code takes place in the `BeginBlock` and `EndBlock` functions that are called at the beginning and at the end of every block. They are powerful tools, but it is important for application developers to be careful with them. For example, it is crutial that developers control the amount of computing that happens in these functions, as expensive computation could delay the block time, and never-ending loop freeze the chain altogether. + +`BeginBlock` and `EndBlock` are composable functions, meaning that each module can implement its own `BeginBlock` and `EndBlock` logic. When needed, `BeginBlock` and `EndBlock` logic is implemented in the module's `handler`. Here is the standard way to proceed for `EndBlock` (`BeginBlock` follows the exact same pattern): + +```go +func NewEndBlocker(k Keeper) sdk.EndBlocker { + return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + err := checkProposal(ctx, k) + if err != nil { + panic(err) + } + return + } +} +``` + +Do not forget that each module need to declare its `BeginBlock` and `EndBlock` constructors at application level. See the [Application - Bridging it all together](app-structure.md). + +For the purpose of our simple governance application, we will use `EndBlock` to automatically tally the results of the vote. Here are the different steps that will be performed: + +1. Get the oldest proposal from the `ProposalProcessingQueue` +2. Check if the `CurrentBlock` is the block at which the voting period for this proposal ends. If Yes, go to 3.. If no, exit. +3. Check if proposal is accepted or rejected. Update the proposal status. +4. Pop the proposal from the `ProposalProcessingQueue` and go back to 1. + +Let us perform a quick safety analysis on this process. +- The loop will not run forever because the number of proposals in `ProposalProcessingQueue` is finite +- The computation should not be too expensive because tallying of individual proposals is not expensive and the number of proposals is expected be relatively low. That is because proposals require a `Deposit` to be accepted. `MinDeposit` should be high enough so that we don't have too many `Proposals` in the queue. +- In the eventuality that the application becomes so successful that the `ProposalProcessingQueue` ends up containing so many proposals that the blockchain starts slowing down, the module should be modified to mitigate the situation. One clever way of doing it is to cap the number of iteration per individual `EndBlock` at `MaxIteration`. This way, tallying will be spread over many blocks if the number of proposals is too important and block time should remain stable. This would require to modify the current check `if (CurrentBlock == Proposal.SubmitBlock + VotingPeriod)` to `if (CurrentBlock > Proposal.SubmitBlock + VotingPeriod) AND (Proposal.Status == ProposalStatusActive)`. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-init.md b/docs/sdk/sdk-by-examples/simple-governance/module-init.md new file mode 100644 index 000000000..d018b512f --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-init.md @@ -0,0 +1,31 @@ +## Module initialization + +First, let us go into the module's folder and create a folder for our module. + +```bash +cd x/ +mkdir simple_governance +cd simple_governance +mkdir -p client/cli client/rest +touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go wire.go +``` + +Let us start by adding the files we will need. Your module's folder should look something like that: + +``` +x +└─── simple_governance + ├─── client + │ ├─── cli + │ │ └─── simple_governance.go + │ └─── rest + │ └─── simple_governance.go + ├─── errors.go + ├─── handler.go + ├─── keeper_keys.go + ├─── keeper.go + ├─── types.go + └─── wire.go +``` + +Let us go into the detail of each of these files. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md b/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md new file mode 100644 index 000000000..851593a78 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md @@ -0,0 +1,96 @@ +## Keeper + +**File: [`x/simple_governance/keeper.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/keeper.go)** + +### Short intro to keepers + +`Keepers` are a module abstraction that handle reading/writing to the module store. This is a practical implementation of the **Object Capability Model** for Cosmos. + + +As module developers, we have to define keepers to interact with our module's store(s) not only from within our module, but also from other modules. When another module wants to access one of our module's store(s), a keeper for this store has to be passed to it at the application level. In practice, it will look like that: + +```go +// in app.go + +// instanciate keepers +keeperA = moduleA.newKeeper(app.moduleAStoreKey) +keeperB = moduleB.newKeeper(app.moduleBStoreKey) + +// pass instance of keeperA to handler of module B +app.Router(). + AddRoute("moduleA", moduleA.NewHandler(keeperA)). + AddRoute("moduleB", moduleB.NewHandler(keeperB, keeperA)) // Here module B can access one of module A's store via the keeperA instance +``` + +`KeeperA` grants a set of capabilities to the handler of module B. When developing a module, it is good practice to think about the sensitivity of the different capabilities that can be granted through keepers. For example, some module may need to read and write to module A's main store, while others only need to read it. If a module has multiple stores, then some keepers could grant access to all of them, while others would only grant access to specific sub-stores. It is the job of the module developer to make sure it is easy for application developers to instanciate a keeper with the right capabilities. Of course, the handler of a module will most likely get an unrestricted instance of that module's keeper. + +### Store for our app + +Before we delve into the keeper itself, let us see what objects we need to store in our governance sub-store, and how to index them. + +- `Proposals` will be indexed by `'proposals'|<proposalID>`. +- `Votes` (`Yes`, `No`, `Abstain`) will be indexed by `'proposals'|<proposalID>|'votes'|<voterAddress>`. + +Notice the quote mark on `'proposals'` and `'votes'`. They indicate that these are constant keywords. So, for example, the option casted by voter with address `0x01` on proposal `0101` will be stored at index `'proposals'|0101|'votes'|0x01`. + +These keywords are used to faciliate range queries. Range queries (TODO: Link to formal spec) allow developer to query a subspace of the store, and return an iterator. They are made possible by the nice properties of the [IAVL+ tree](https://github.com/tendermint/iavl) that is used in the background. In practice, this means that it is possible to store and query a Key-Value pair in O(1), while still being able to iterate over a given subspace of Key-Value pairs. For example, we can query all the addresses that voted on a given proposal, along with their votes, by calling `rangeQuery(SimpleGovStore, <proposalID|'addresses'>)`. + +### Keepers for our app + +In our case, we only have one store to access, the `SimpleGov` store. We will need to set and get values inside this store via our keeper. However, these two actions do not have the same impact in terms of security. While there should no problem in granting read access to our store to other modules, write access is way more sensitive. So ideally application developers should be able to create either a governance mapper that can only get values from the store, or one that can both get and set values. To this end, we will introduce two keepers: `Keeper` and `KeeperRead`. When application developers create their application, they will be able to decide which of our module's keeper to use. + +Now, let us try to think about which keeper from **external** modules our module's keepers need access to. +Each proposal requires a deposit. This means our module needs to be able to both read and write to the module that handles tokens, which is the `bank` module. We also need to be able to determine the voting power of each voter based on their stake. To this end, we need read access to the store of the `staking` module. However, we don't need write access to this store. We should therefore indicate that in our module, and the application developer should be careful to only pass a read-only keeper of the `staking` module to our module's handler. + +With all that in mind, we can define the structure of our `Keeper`: + +```go + type Keeper struct { + SimpleGov sdk.StoreKey // Key to our module's store + cdc *wire.Codec // Codec to encore/decode structs + ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance + sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store. + codespace sdk.CodespaceType // Reserves space for error codes + } +``` + +And the structure of our `KeeperRead`: + +```go +type KeeperRead struct { + Keeper +} +``` + +`KeeperRead` will inherit all methods from `Keeper`, except those that we override. These will be the methods that perform writes to the store. + +### Functions and Methods + +The first function we have to create is the constructor. + +```go +func NewKeeper(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) Keeper +``` + +This function is called from the main [`app.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/app/app.go) file to instanciate a new `Keeper`. A similar function exits for `KeeperRead`. + +```go +func NewKeeperRead(SimpleGov sdk.StoreKey, ck bank.Keeper, sm stake.Keeper, codespace sdk.CodespaceType) KeeperRead +``` + +Depending on the needs of the application and its modules, either `Keeper`, `KeeperRead`, or both, will be instanciated at application level. + +*Note: Both the `Keeper` type name and `NewKeeper()` function's name are standard names used in every module. It is no requirement to follow this standard, but doing so can facilitate the life of application developers* + +Now, let us describe the methods we need for our module's `Keeper`. For the full implementation, please refer to `keeper.go`. + +- `GetProposal`: Get a `Proposal` given a `proposalID`. Proposals need to be decoded from `byte` before they can be read. +- `SetProposal`: Set a `Proposal` at index `'proposals'|<proposalID>`. Proposals need to be encoded to `byte` before they can be stored. +- `NewProposalID`: A function to generate a new unique `proposalID`. +- `GetVote`: Get a vote `Option` given a `proposalID` and a `voterAddress`. +- `SetVote`: Set a vote `Option` given a `proposalID` and a `voterAddress`. +- Proposal Queue methods: These methods implement a standard proposal queue to store `Proposals` on a First-In First-Out basis. It is used to tally the votes at the end of the voting period. + +The last thing that needs to be done is to override certain methods for the `KeeperRead` type. `KeeperRead` should not have write access to the stores. Therefore, we will override the methods `SetProposal()`, `SetVote()` and `NewProposalID()`, as well as `setProposalQueue()` from the Proposal Queue's methods. For `KeeperRead`, these methods will just throw an error. + +*Note: If you look at the code, you'll notice that the context `ctx` is a parameter of many of the methods. The context `ctx` provides useful information on the current state such as the current block height and allows the keeper `k` to access the `KVStore`. You can check all the methods of `ctx` [here](https://github.com/cosmos/cosmos-sdk/blob/develop/types/context.go#L144-L168)*. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-rest.md b/docs/sdk/sdk-by-examples/simple-governance/module-rest.md new file mode 100644 index 000000000..51758f2f4 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-rest.md @@ -0,0 +1,32 @@ +## Rest API + +**File: [`x/simple_governance/client/rest/simple_governance.goo`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/client/rest/simple_governance.go)** + +The Rest Server, also called [Light-Client Daemon (LCD)](https://github.com/cosmos/cosmos-sdk/tree/master/client/lcd), provides support for **HTTP queries**. + +________________________________________________________ + +USER INTERFACE <=======> REST SERVER <=======> FULL-NODE + +________________________________________________________ + +It allows end-users that do not want to run full-nodes themselves to interract with the chain. The LCD can be configured to perform **Light-Client verification** via the flag `--trust-node`, which can be set to `true` or `false`. + +- If *light-client verification* is enabled, the Rest Server acts as a light-client and needs to be run on the end-user's machine. It allows them to interract with the chain in a trustless way without having to store the whole chain locally. + +- If *light-client verification* is disabled, the Rest Server acts as a simple relayer for HTTP calls. In this setting, the Rest server needs not be run on the end-user's machine. Instead, it will probably be run by the same entity that operates the full-node the server connects to. This mode is useful if end-users trust the full-node operator and do not want to store anything locally. + +Now, let us define endpoints that will be available for users to query through HTTP requests. These endpoints will be defined in a `simple_governance.go` file stored in the `rest` folder. + +| Method | URL | Description | +|--------|---------------------------------|-------------------------------------------------------------| +| GET | /proposals | Range query to get all submitted proposals | +| POST | /proposals | Submit a new proposal | +| GET | /proposals/{id} | Returns a proposal given its ID | +| GET | /proposals/{id}/votes | Range query to get all the votes casted on a given proposal | +| POST | /proposals/{id}/votes | Cast a vote on a given proposal | +| GET | /proposals/{id}/votes/{address} | Returns the vote of a given address on a given proposal | + +It is the job of module developers to provide sensible endpoints so that front-end developers and service providers can properly interact with it. + +Additionaly, here is a [link](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9) for REST APIs best practices. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-types.md b/docs/sdk/sdk-by-examples/simple-governance/module-types.md new file mode 100644 index 000000000..3b08448a6 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-types.md @@ -0,0 +1,23 @@ +## Types + +**File: [`x/simple_governance/types.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/types.go)** + +In this file, we define the custom types for our module. This includes the types from the [State](app-design.md#State) section and the custom message types defined in the [Messages](app-design#Messages) section. + +For each new type that is not a message, it is possible to add methods that make sense in the context of the application. In our case, we will implement an `updateTally` function to easily update the tally of a given proposal as vote messages come in. + +Messages are a bit different. They implement the `Message` interface defined in the SDK's `types` folder. Here are the methods you need to implement when you define a custom message type: + +- `Type()`: This function returns the name of our module's route. When messages are processed by the application, they are routed using the string returned by the `Type()` method. +- `GetSignBytes()`: Returns the byte representation of the message. It is used to sign the message. +- `GetSigners()`: Returns address(es) of the signer(s). +- `ValidateBasic()`: This function is used to discard obviously invalid messages. It is called at the beginning of `runTx()` in the baseapp file. If `ValidateBasic()` does not return `nil`, the app stops running the transaction. +- `Get()`: A basic getter, returns some property of the message. +- `String()`: Returns a human-readable version of the message + +For our simple governance messages, this means: + +- `Type()` will return `"simpleGov"` +- For `SubmitProposalMsg`, we need to make sure that the attributes are not empty and that the deposit is both valid and positive. Note that this is only basic validation, we will therefore not check in this method that the sender has sufficient funds to pay for the deposit +- For `VoteMsg`, we check that the address and option are valid and that the proposalID is not negative. +- As for other methods, less customization is required. You can check the code to see a standard way of implementing these. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-wire.md b/docs/sdk/sdk-by-examples/simple-governance/module-wire.md new file mode 100644 index 000000000..f889db91a --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-wire.md @@ -0,0 +1,13 @@ +## Wire + +**File: [`x/simple_governance/wire.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/wire.go)** + +The `wire.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare: + +```go +func RegisterWire(cdc *wire.Codec) { + cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil) + cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil) +} +``` +Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more). \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/run-install.md b/docs/sdk/sdk-by-examples/simple-governance/run-install.md new file mode 100644 index 000000000..dbbf203de --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/run-install.md @@ -0,0 +1,18 @@ +## Installation + +Once you have finallized your application, install it using `go get`. The following commands will install the pre-built modules and examples of the SDK as well as your `simpleGov` application: + +```bash +go get github.com/<your_username>/cosmos-sdk +cd $GOPATH/src/github.com/<your_username>/cosmos-sdk +make get_vendor_deps +make install +make install_examples +``` + +Check that the app is correctly installed by typing: + +```bash +simplegovcli -h +simplegovd -h +``` \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/setup.md b/docs/sdk/sdk-by-examples/simple-governance/setup.md new file mode 100644 index 000000000..963c7bf81 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/setup.md @@ -0,0 +1,32 @@ +## Setup + +### Prerequisites + +- Have [go](https://golang.org/dl/) and [git](https://git-scm.com/downloads) installed +- Don't forget to set your `PATH` and `GOPATH` + +### Setup work environment + +Go to the [Cosmos-SDK repo](https://githum.com/cosmos/cosmos-sdk) and fork it. Then open a terminal and: + +```bash +cd $GOPATH/src/github.com/your_username +git clone github.com/your_username/cosmos-sdk +cd cosmos-sdk +``` + +Now we'll add the origin Cosmos-SDK as upstream in case some cool feature or module gets merged: + +```bash +git remote add upstream github.com/cosmos/cosmos-sdk +git fetch upstream +git rebase upstream/master +``` + +We will also create a branch dedicated to our module: + +```bash +git checkout -b my_new_application +``` + +We are all set! \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/start.md b/docs/sdk/sdk-by-examples/simple-governance/start.md new file mode 100644 index 000000000..8461c84d9 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/start.md @@ -0,0 +1,10 @@ +## Starting your own project + +To get started, you just have to follow these simple steps: + +1. Clone the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/tree/develop)repo +2. Code the modules needed by your application that do not already exist. +3. Create your app directory. In the app main file, import the module you need and instantiate the different stores. +4. Launch your blockchain. + +Easy as pie! With the introduction over, let us delve into practice and learn how to code a SDK application with an example. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md new file mode 100644 index 000000000..bb9eb289f --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md @@ -0,0 +1,19 @@ +## Submit a proposal + +Uuse the CLI to create a new proposal: + +```bash +simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms +``` + +Get the details of your newly created proposal: + +```bash +simplegovcli proposal 1 +``` + +You can also check all the existing open proposals: + +```bash +simplegovcli proposals --active=true +``` \ No newline at end of file diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index c61fd1e3f..728ded371 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -25,9 +25,9 @@ type VotingProcedure struct { ```go type TallyingProcedure struct { - Threshold rational.Rational // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 - Veto rational.Rational // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 - GovernancePenalty sdk.Rat // Penalty if validator does not vote + Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 + Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 + GovernancePenalty sdk.Dec // Penalty if validator does not vote GracePeriod int64 // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -81,7 +81,7 @@ This type is used in a temp map when tallying ```go type ValidatorGovInfo struct { - Minus sdk.Rat + Minus sdk.Dec Vote Vote } ``` @@ -103,17 +103,17 @@ type Proposal struct { VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached CurrentStatus ProposalStatus // Current status of the proposal - YesVotes sdk.Rat - NoVotes sdk.Rat - NoWithVetoVotes sdk.Rat - AbstainVotes sdk.Rat + YesVotes sdk.Dec + NoVotes sdk.Dec + NoWithVetoVotes sdk.Dec + AbstainVotes sdk.Dec } ``` We also mention a method to update the tally for a given proposal: ```go - func (proposal Proposal) updateTally(vote byte, amount sdk.Rat) + func (proposal Proposal) updateTally(vote byte, amount sdk.Dec) ``` ### Stores diff --git a/docs/spec/inflation/state.md b/docs/spec/inflation/state.md index dea10c046..c16286804 100644 --- a/docs/spec/inflation/state.md +++ b/docs/spec/inflation/state.md @@ -7,7 +7,7 @@ The current annual inflation rate. ```golang -type Inflation sdk.Rat +type Inflation sdk.Dec ``` ### InflationLastTime diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index b40b06b92..0eb557fec 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -16,4 +16,3 @@ EndBlock() ValidatorSetChanges ClearTendermintUpdates() return vsc ``` - diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 376a38ced..46f18ed78 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -13,7 +13,7 @@ type Pool struct { LooseTokens int64 // tokens not associated with any bonded validator BondedTokens int64 // reserve of bonded tokens InflationLastTime int64 // block which the last inflation was processed // TODO make time - Inflation sdk.Rat // current annual inflation rate + Inflation sdk.Dec // current annual inflation rate DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily) } @@ -28,10 +28,10 @@ overall functioning of the stake module. ```golang type Params struct { - InflationRateChange sdk.Rat // maximum annual change in inflation rate - InflationMax sdk.Rat // maximum inflation rate - InflationMin sdk.Rat // minimum inflation rate - GoalBonded sdk.Rat // Goal of percent bonded atoms + InflationRateChange sdk.Dec // maximum annual change in inflation rate + InflationMax sdk.Dec // maximum inflation rate + InflationMin sdk.Dec // minimum inflation rate + GoalBonded sdk.Dec // Goal of percent bonded atoms MaxValidators uint16 // maximum number of validators BondDenom string // bondable coin denomination @@ -74,9 +74,9 @@ type Validator struct { Revoked bool // has the validator been revoked? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat // total shares issued to a validator's delegators - SlashRatio sdk.Rat // increases each time the validator is slashed + Tokens sdk.Dec // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec // total shares issued to a validator's delegators + SlashRatio sdk.Dec // increases each time the validator is slashed Description Description // description terms for the validator @@ -88,10 +88,10 @@ type Validator struct { } type CommissionInfo struct { - Rate sdk.Rat // the commission rate of fees charged to any delegators - Max sdk.Rat // maximum commission rate which this validator can ever charge - ChangeRate sdk.Rat // maximum daily increase of the validator commission - ChangeToday sdk.Rat // commission rate change today, reset each day (UTC time) + Rate sdk.Dec // the commission rate of fees charged to any delegators + Max sdk.Dec // maximum commission rate which this validator can ever charge + ChangeRate sdk.Dec // maximum daily increase of the validator commission + ChangeToday sdk.Dec // commission rate change today, reset each day (UTC time) LastChange int64 // unix timestamp of last commission change } @@ -117,7 +117,7 @@ the transaction is the owner of the bond. ```golang type Delegation struct { - Shares sdk.Rat // delegation shares recieved + Shares sdk.Dec // delegation shares recieved Height int64 // last height bond updated } ``` @@ -178,8 +178,8 @@ the original redelegation has been completed. ```golang type Redelegation struct { - SourceShares sdk.Rat // amount of source shares redelegating - DestinationShares sdk.Rat // amount of destination shares created at redelegation + SourceShares sdk.Dec // amount of source shares redelegating + DestinationShares sdk.Dec // amount of destination shares created at redelegation CompleteTime int64 // unix time to complete redelegation } ``` diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 4f2567958..d1b6a3626 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -18,7 +18,7 @@ Other notes: - `sender` denotes the address of the sender of the transaction - `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and modify objects from the store - - `sdk.Rat` refers to a rational numeric type specified by the SDK. + - `sdk.Dec` refers to a decimal type specified by the SDK. ### TxCreateValidator @@ -34,9 +34,9 @@ type TxCreateValidator struct { SelfDelegation coin.Coin Description Description - Commission sdk.Rat - CommissionMax sdk.Rat - CommissionMaxChange sdk.Rat + Commission sdk.Dec + CommissionMax sdk.Dec + CommissionMaxChange sdk.Dec } @@ -65,7 +65,7 @@ If either the `Description` (excluding `DateBonded` which is constant), ```golang type TxEditCandidacy struct { GovernancePubKey crypto.PubKey - Commission sdk.Rat + Commission sdk.Dec Description Description } @@ -199,7 +199,7 @@ type TxRedelegate struct { DelegatorAddr Address ValidatorFrom Validator ValidatorTo Validator - Shares sdk.Rat + Shares sdk.Dec CompletedTime int64 } diff --git a/docs/validators/validator-faq.md b/docs/validators/validator-faq.md index eea661042..6018ac323 100644 --- a/docs/validators/validator-faq.md +++ b/docs/validators/validator-faq.md @@ -14,9 +14,9 @@ The [Cosmos Hub](/introduction/cosmos-hub.md) is based on [Tendermint](/introduc 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. -Any user in the system can declare its intention to become a validator by sending a "declare-candidacy" transaction. From there, they become validator candidates. +Any user in the system can declare its intention to become a validator by sending a `create-validator` transaction. From there, they become validators. -The weight (i.e. total stake) of a candidate determines wether or not it is a 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 validator candidates with the most weight will be 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. 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'. ### What is a full-node? @@ -28,7 +28,7 @@ Of course, it is possible and encouraged for any user to run full-nodes even if 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). -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 validator candidates before delegating, as well as spreading their stake over multiple validators. +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. 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. @@ -36,21 +36,22 @@ Delegators play a critical role in the system, as they are responsible for choos ### How to become a validator? -Any participant in the network can signal that they want to become a validator by sending a "declare-candidacy" transaction, where they must fill out the following parameters: +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 validator must signal an account with which it will perform its validator duties. 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 name +* 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 candidate can charge -* Commission change rate: The maximum daily increase of the validator candidate commission -* Minimum self-bond amount: Minimum amount of Atoms the validator candidate 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 Atoms the validator candidate wants to self-bond +* 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 -Once a PubKey has declared candidacy, 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 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. -Out of all the candidates that signaled themselves, the 100 with the most stake are the ones who are designated as validators. If a validator's total stake falls below the top 100 then that validator loses its validator privileges. Over time, the maximum number of validators will increase, according to a predefined schedule: +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: * **Year 0:** 100 * **Year 1:** 113 @@ -64,19 +65,48 @@ Out of all the candidates that signaled themselves, the 100 with the most stake * **Year 9:** 300 * **Year 10:** 300 +## Testnet + ### How can I join the testnet? 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 and more here](/getting-started/full-node.md). +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). + +### 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 `cosmosvalpub`. + + Generated when the node is created with gaiad init. + + Get this value with gaiad tendermint show_validator + +M e.g. cosmosvalpub1zcjduc3qcyj09qc03elte23zwshdx92jm6ce88fgc90rtqhjx8v0608qh5ssp0w94c + +- **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 `cosmosaccpub` and an address `cosmosaccaddr`. Both are derived from account keys generated by `gaiacli keys add`. + + +### 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: + +- `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. +- `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-bond'? How can I increase my 'self-bond'? ### Is there a faucet? -If you want to obtain coins for the testnet, you can do so by using [this faucet](https://faucetcosmos.network) +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 a validator? +### Is there a minimum amount of Atoms that must be staked to be an active (=bonded) validator? -There is no minimum. The top 100 validator candidates with the highest total stake (where total stake = self-bonded stake + delegators stake) are the validators. +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. ### How will delegators choose their validators? @@ -87,7 +117,7 @@ Delegators are free to choose validators according to their own subjective crite * **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. +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) ## Responsibilites @@ -199,9 +229,9 @@ If a validator misbehaves, its bonded stake along with its delegators' stake and * **Double signing:** If someone reports on chain A that a validator signed two blocks at the same height on chain A and chain B, 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 and once the fault is reported by a someone, its stake will receive a minor slash. +* **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. A complete document on the economics of the network will be published soon. +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-bond Atoms? @@ -251,7 +281,6 @@ Validators should expect to run an HSM that supports ed25519 keys. Here are pote * Ledger Nano S * Ledger BOLOS SGX enclave * Thales nShield support -* Tendermint SGX enclave The Tendermint team does not recommend one solution above the other. The community is encouraged to bolster the effort to improve HSMs and the security of key management. @@ -276,3 +305,5 @@ Validator nodes should only connect to full-nodes they trust because they operat Sentry nodes can be quickly spun up or change their IP addresses. Because the links to the sentry nodes are in private IP space, an internet based attacked cannot disturb them directly. This will ensure validator block proposals and votes always make it to the rest of the network. It is expected that good operating procedures on that part of validators will completely mitigate these threats. + +For more on sentry node architecture, see [this](https://forum.cosmos.network/t/sentry-node-architecture-overview/454). diff --git a/docs/validators/validator-setup.md b/docs/validators/validator-setup.md index dad18151e..dc3bf147f 100644 --- a/docs/validators/validator-setup.md +++ b/docs/validators/validator-setup.md @@ -1,5 +1,9 @@ # Validator Setup +::: warning Current Testnet +The current testnet is `gaia-7005`. +::: + Before setting up your validator node, make sure you've already gone through the [Full Node Setup](/getting-started/full-node.md) guide. ## Running a Validator Node @@ -15,7 +19,7 @@ If you want to become a validator for the Hub's `mainnet`, you should [research Your `cosmosvalpub` can be used to create a new validator by staking tokens. You can find your validator pubkey by running: ```bash -gaiad tendermint show_validator +gaiad tendermint show-validator ``` Next, craft your `gaiacli stake create-validator` command: @@ -27,10 +31,10 @@ Don't use more `steak` thank you have! You can always get more by using the [Fau ```bash gaiacli stake create-validator \ --amount=5steak \ - --pubkey=$(gaiad tendermint show_validator) \ + --pubkey=$(gaiad tendermint show-validator) \ --address-validator=<account_cosmosaccaddr> --moniker="choose a moniker" \ - --chain-id=gaia-6002 \ + --chain-id=gaia-7005 \ --name=<key_name> ``` @@ -47,17 +51,18 @@ gaiacli stake edit-validator --website="https://cosmos.network" \ --identity=6A0D65E29A4CBC8E --details="To infinity and beyond!" - --chain-id=gaia-6002 \ + --chain-id=gaia-7005 \ --name=<key_name> ``` ### View Validator Description + View the validator's information with this command: ```bash gaiacli stake validator \ --address-validator=<account_cosmosaccaddr> \ - --chain-id=gaia-6002 + --chain-id=gaia-7005 ``` ### Confirm Your Validator is Running @@ -65,7 +70,7 @@ gaiacli stake validator \ Your validator is active if the following command returns anything: ```bash -gaiacli advanced tendermint validator-set | grep "$(gaiad tendermint show_validator)" +gaiacli advanced tendermint validator-set | grep "$(gaiad tendermint show-validator)" ``` You should also be able to see your validator on the [Explorer](https://explorecosmos.network/validators). You are looking for the `bech32` encoded `address` in the `~/.gaiad/config/priv_validator.json` file. @@ -79,7 +84,7 @@ To be in the validator set, you need to have more total voting power than the 10 ### Problem #1: My validator has `voting_power: 0` -Your validator has become auto-unbonded. In `gaia-6002`, we unbond validators if they do not vote on `50` of the last `100` blocks. Since blocks are proposed every ~2 seconds, a validator unresponsive for ~100 seconds will become unbonded. This usually happens when your `gaiad` process crashes. +Your validator has become auto-unbonded. In `gaia-7005`, we unbond validators if they do not vote on `50` of the last `100` blocks. Since blocks are proposed every ~2 seconds, a validator unresponsive for ~100 seconds will become unbonded. This usually happens when your `gaiad` process crashes. Here's how you can return the voting power back to your validator. First, if `gaiad` is not running, start it up again: @@ -90,7 +95,7 @@ gaiad start Wait for your full node to catch up to the latest block. Next, run the following command. Note that `<cosmosaccaddr>` is the address of your validator account, and `<name>` is the name of the validator account. You can find this info by running `gaiacli keys list`. ```bash -gaiacli stake unrevoke <cosmosaccaddr> --chain-id=gaia-6002 --name=<name> +gaiacli stake unrevoke <cosmosaccaddr> --chain-id=gaia-7005 --name=<name> ``` ::: danger Warning diff --git a/examples/README.md b/examples/README.md index e3016fb70..d12e3d3fc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -104,9 +104,9 @@ You should now see alice, bob and charlie's account all show up. ``` NAME: ADDRESS: PUBKEY: -alice 90B0B9BE0914ECEE0B6DB74E67B07A00056B9BBD 1624DE62201D47E63694448665F5D0217EA8458177728C91C373047A42BD3C0FB78BD0BFA7 -bob 29D721F054537C91F618A0FDBF770DA51EF8C48D 1624DE6220F54B2A2CA9EB4EE30DE23A73D15902E087C09CC5616456DDDD3814769E2E0A16 -charlie 2E8E13EEB8E3F0411ACCBC9BE0384732C24FBD5E 1624DE6220F8C9FB8B07855FD94126F88A155BD6EB973509AE5595EFDE1AF05B4964836A53 +alice cosmosaccaddr1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f cosmosaccpub1addwnpepq0w037u5g7y7lvdvsred2dehg90j84k0weyss5ynysf0nnnax74agrsxns6 +bob cosmosaccaddr18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz cosmosaccpub1addwnpepqwe97n8lryxrzvamrvjfj24jys3uzf8wndfvqa2l7mh5nsv4jrvdznvyeg6 +charlie cosmosaccaddr13wq5mklhn03ljpd4dkph5rflk5a3ssma2ag07q cosmosaccpub1addwnpepqdmtxv35rrmv2dvcr3yhfyxj7dzrd4z4rnhmclksq4g55a4wpl54clvx33l ``` @@ -115,15 +115,15 @@ charlie 2E8E13EEB8E3F0411ACCBC9BE0384732C24FBD5E 1624DE6220F8C9FB8B07855FD94126F Lets send bob and charlie some tokens. First, lets query alice's account so we can see what kind of tokens she has: ``` -basecli account 90B0B9BE0914ECEE0B6DB74E67B07A00056B9BBD +basecli account cosmosaccaddr1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f ``` -Where `90B0B9BE0914ECEE0B6DB74E67B07A00056B9BBD` is alice's address we got from running `basecli keys list`. You should see a large amount of "mycoin" there. If you search for bob's or charlie's address, the command will fail, because they haven't been added into the blockchain database yet since they have no coins. We need to send them some! +Where `cosmosaccaddr1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f` is alice's address we got from running `basecli keys list`. You should see a large amount of "mycoin" there. If you search for bob's or charlie's address, the command will fail, because they haven't been added into the blockchain database yet since they have no coins. We need to send them some! The following command will send coins from alice, to bob: ``` -basecli send --from=alice --amount=10000mycoin --to=29D721F054537C91F618A0FDBF770DA51EF8C48D +basecli send --from=alice --amount=10000mycoin --to=cosmosaccaddr18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz --sequence=0 --chain-id=test-chain-AE4XQo ``` @@ -136,13 +136,13 @@ Flag Descriptions: Now if we check bobs account, it should have `10000 mycoin`. You can do so by running : ``` -basecli account 29D721F054537C91F618A0FDBF770DA51EF8C48D +basecli account cosmosaccaddr18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz ``` Now lets send some from bob to charlie. Make sure you send less than bob has, otherwise the transaction will fail: ``` -basecli send --from=bob --amount=5000mycoin --to=2E8E13EEB8E3F0411ACCBC9BE0384732C24FBD5E +basecli send --from=bob --amount=5000mycoin --to=cosmosaccaddr13wq5mklhn03ljpd4dkph5rflk5a3ssma2ag07q --sequence=0 --chain-id=test-chain-AE4XQo ``` @@ -151,7 +151,7 @@ Note how we use the ``--from`` flag to select a different account to send from. Lets now try to send from bob back to alice: ``` -basecli send --from=bob --amount=3000mycoin --to=90B0B9BE0914ECEE0B6DB74E67B07A00056B9BBD +basecli send --from=bob --amount=3000mycoin --to=cosmosaccaddr1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f --sequence=1 --chain-id=test-chain-AE4XQo ``` @@ -179,7 +179,7 @@ starting this tutorial again or trying something new), the following commands are run: ``` -basecoind unsafe_reset_all +basecoind unsafe-reset-all rm -rf ~/.basecoind rm -rf ~/.basecli ``` @@ -279,7 +279,7 @@ type TxInput struct { Address []byte `json:"address"` // Hash of the PubKey Coins Coins `json:"coins"` // Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput - Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx + Signature []byte `json:"signature"` // Depends on the PubKey type and the whole Tx PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0 } diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 4b1eef258..17f6de87b 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -62,8 +62,8 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba // define and attach the mappers and keepers app.accountMapper = auth.NewAccountMapper( cdc, - app.keyAccount, // target store - func () auth.Account { + app.keyAccount, // target store + func() auth.Account { return &types.AppAccount{} }, ) diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index 0ef0a2391..789a1cb6a 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -10,7 +10,7 @@ import ( // Validator implements sdk.Validator type Validator struct { Address sdk.AccAddress - Power sdk.Rat + Power sdk.Dec } // Implements sdk.Validator @@ -29,18 +29,18 @@ func (v Validator) GetPubKey() crypto.PubKey { } // Implements sdk.Validator -func (v Validator) GetTokens() sdk.Rat { - return sdk.ZeroRat() +func (v Validator) GetTokens() sdk.Dec { + return sdk.ZeroDec() } // Implements sdk.Validator -func (v Validator) GetPower() sdk.Rat { +func (v Validator) GetPower() sdk.Dec { return v.Power } // Implements sdk.Validator -func (v Validator) GetDelegatorShares() sdk.Rat { - return sdk.ZeroRat() +func (v Validator) GetDelegatorShares() sdk.Dec { + return sdk.ZeroDec() } // Implements sdk.Validator @@ -93,8 +93,8 @@ func (vs *ValidatorSet) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) } // TotalPower implements sdk.ValidatorSet -func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Rat { - res := sdk.ZeroRat() +func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Dec { + res := sdk.ZeroDec() for _, val := range vs.Validators { res = res.Add(val.Power) } @@ -122,7 +122,7 @@ func (vs *ValidatorSet) RemoveValidator(addr sdk.AccAddress) { } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, power int64, amt sdk.Rat) { +func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, power int64, amt sdk.Dec) { panic("not implemented") } diff --git a/examples/democoin/x/assoc/validator_set_test.go b/examples/democoin/x/assoc/validator_set_test.go index 2fead3ad2..eac23b25e 100644 --- a/examples/democoin/x/assoc/validator_set_test.go +++ b/examples/democoin/x/assoc/validator_set_test.go @@ -32,8 +32,8 @@ func TestValidatorSet(t *testing.T) { addr2 := []byte("addr2") base := &mock.ValidatorSet{[]mock.Validator{ - {addr1, sdk.NewRat(1)}, - {addr2, sdk.NewRat(2)}, + {addr1, sdk.NewDec(1)}, + {addr2, sdk.NewDec(2)}, }} valset := NewValidatorSet(wire.NewCodec(), ctx.KVStore(key).Prefix([]byte("assoc")), base, 1, 5) diff --git a/examples/democoin/x/oracle/README.md b/examples/democoin/x/oracle/README.md index eec02d724..0cfcb820d 100644 --- a/examples/democoin/x/oracle/README.md +++ b/examples/democoin/x/oracle/README.md @@ -38,7 +38,7 @@ func NewHandler(keeper Keeper) sdk.Handler { In the previous example, the keeper has an `oracle.Keeper`. `oracle.Keeper`s are generated by `NewKeeper`. ```go -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { return Keeper { cdc: cdc, key: key, diff --git a/examples/democoin/x/oracle/handler.go b/examples/democoin/x/oracle/handler.go index 079f7680f..3c78fc566 100644 --- a/examples/democoin/x/oracle/handler.go +++ b/examples/democoin/x/oracle/handler.go @@ -23,7 +23,7 @@ func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.Valid // and recalculate voted power hash := ctx.BlockHeader().ValidatorsHash if !bytes.Equal(hash, info.Hash) { - info.Power = sdk.ZeroRat() + info.Power = sdk.ZeroDec() info.Hash = hash prefix := GetSignPrefix(p, keeper.cdc) store := ctx.KVStore(keeper.key) diff --git a/examples/democoin/x/oracle/keeper.go b/examples/democoin/x/oracle/keeper.go index 25554faa8..0406f560a 100644 --- a/examples/democoin/x/oracle/keeper.go +++ b/examples/democoin/x/oracle/keeper.go @@ -13,12 +13,12 @@ type Keeper struct { valset sdk.ValidatorSet - supermaj sdk.Rat + supermaj sdk.Dec timeout int64 } // NewKeeper constructs a new keeper -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { if timeout < 0 { panic("Timeout should not be negative") } @@ -46,7 +46,7 @@ const ( // Info for each payload type Info struct { - Power sdk.Rat + Power sdk.Dec Hash []byte LastSigned int64 Status InfoStatus @@ -55,7 +55,7 @@ type Info struct { // EmptyInfo construct an empty Info func EmptyInfo(ctx sdk.Context) Info { return Info{ - Power: sdk.ZeroRat(), + Power: sdk.ZeroDec(), Hash: ctx.BlockHeader().ValidatorsHash, LastSigned: ctx.BlockHeight(), Status: Pending, diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index c476290f8..f4971c8b1 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -107,9 +107,9 @@ func TestOracle(t *testing.T) { addr3 := []byte("addr3") addr4 := []byte("addr4") valset := &mock.ValidatorSet{[]mock.Validator{ - {addr1, sdk.NewRat(7)}, - {addr2, sdk.NewRat(7)}, - {addr3, sdk.NewRat(1)}, + {addr1, sdk.NewDec(7)}, + {addr2, sdk.NewDec(7)}, + {addr3, sdk.NewDec(1)}, }} key := sdk.NewKVStoreKey("testkey") @@ -119,7 +119,7 @@ func TestOracle(t *testing.T) { require.Nil(t, err) ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz}) - ork := NewKeeper(key, cdc, valset, sdk.NewRat(2, 3), 100) + ork := NewKeeper(key, cdc, valset, sdk.NewDecWithPrec(667, 3), 100) // 66.7% h := seqHandler(ork, key, sdk.CodespaceRoot) // Nonmock.Validator signed, transaction failed @@ -171,7 +171,7 @@ func TestOracle(t *testing.T) { require.Equal(t, 1, getSequence(ctx, key)) // Should handle mock.Validator set change - valset.AddValidator(mock.Validator{addr4, sdk.NewRat(12)}) + valset.AddValidator(mock.Validator{addr4, sdk.NewDec(12)}) bz, err = json.Marshal(valset) require.Nil(t, err) ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz}) diff --git a/examples/kvstore/kvstore b/examples/kvstore/kvstore index 43c7dc61c..5dd8b5eea 100755 Binary files a/examples/kvstore/kvstore and b/examples/kvstore/kvstore differ diff --git a/networks/README-DO.rst b/networks/README-DO.rst deleted file mode 100644 index 9e9275bc1..000000000 --- a/networks/README-DO.rst +++ /dev/null @@ -1,69 +0,0 @@ -Terraform & Ansible -=================== - -WARNING: The Digital Ocean scripts are obsolete. They are here because they might still be useful for developers. - -Automated deployments are done using `Terraform <https://www.terraform.io/>`__ to create servers on Digital Ocean then -`Ansible <http://www.ansible.com/>`__ to create and manage testnets on those servers. - -Prerequisites -------------- - -- Install `Terraform <https://www.terraform.io/downloads.html>`__ and `Ansible <http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html>`__ on a Linux machine. -- Create a `DigitalOcean API token <https://cloud.digitalocean.com/settings/api/tokens>`__ with read and write capability. -- Install the python dopy package (``pip install dopy``) (This is necessary for the digitalocean.py script for ansible.) -- Create SSH keys - -:: - - export DO_API_TOKEN="abcdef01234567890abcdef01234567890" - export TESTNET_NAME="remotenet" - export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" - export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" - -These will be used by both ``terraform`` and ``ansible``. - -Create a remote network ------------------------ - -:: - - make remotenet-start - - -Optionally, you can set the number of servers you want to launch and the name of the testnet (which defaults to remotenet): - -:: - - TESTNET_NAME="mytestnet" SERVERS=7 make remotenet-start - - -Quickly see the /status endpoint --------------------------------- - -:: - - make remotenet-status - - -Delete servers --------------- - -:: - - make remotenet-stop - -Logging -------- - -You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on `this page <https://app.logz.io/#/dashboard/data-sources/Filebeat>`__, then: - -:: - - yum install systemd-devel || echo "This will only work on RHEL-based systems." - apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." - - go get github.com/mheese/journalbeat - ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 - - diff --git a/networks/README.md b/networks/README.md new file mode 100644 index 000000000..322d37340 --- /dev/null +++ b/networks/README.md @@ -0,0 +1,65 @@ +# Terraform & Ansible + +Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on AWS then +[Ansible](http://www.ansible.com/) to create and manage testnets on those servers. + +## Prerequisites + +- Install [Terraform](https://www.terraform.io/downloads.html) and [Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) on a Linux machine. +- Create an [AWS API token](https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html) with EC2 create capability. +- Create SSH keys + +``` +export AWS_ACCESS_KEY_ID="2345234jk2lh4234" +export AWS_SECRET_ACCESS_KEY="234jhkg234h52kh4g5khg34" +export TESTNET_NAME="remotenet" +export CLUSTER_NAME= "remotenetvalidators" +export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" +export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" +``` + +These will be used by both `terraform` and `ansible`. + +## Create a remote network + +``` +SERVERS=1 REGION_LIMIT=1 make validators-start +``` + +The testnet name is what's going to be used in --chain-id, while the cluster name is the administrative tag in AWS for the servers. The code will create SERVERS amount of servers in each availability zone up to the number of REGION_LIMITs, starting at us-east-2. (us-east-1 is excluded.) The below BaSH script does the same, but sometimes it's more comfortable for input. + +``` +./new-testnet.sh "$TESTNET_NAME" "$CLUSTER_NAME" 1 1 +``` + +## Quickly see the /status endpoint + +``` +make validators-status +``` + +## Delete servers + +``` +make validators-stop +``` + +## Logging + +You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on [this page](https://app.logz.io/#/dashboard/data-sources/Filebeat), then: + +``` +yum install systemd-devel || echo "This will only work on RHEL-based systems." +apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." + +go get github.com/mheese/journalbeat +ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 +``` + +## Monitoring + +You can install the DataDog agent with: + +``` +make datadog-install +``` diff --git a/networks/README.rst b/networks/README.rst deleted file mode 100644 index 1e2fd8724..000000000 --- a/networks/README.rst +++ /dev/null @@ -1,78 +0,0 @@ -Terraform & Ansible -=================== - -Automated deployments are done using `Terraform <https://www.terraform.io/>`__ to create servers on AWS then -`Ansible <http://www.ansible.com/>`__ to create and manage testnets on those servers. - -Prerequisites -------------- - -- Install `Terraform <https://www.terraform.io/downloads.html>`__ and `Ansible <http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html>`__ on a Linux machine. -- Create an `AWS API token <https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html>`__ with EC2 create capability. -- Create SSH keys - -:: - - export AWS_ACCESS_KEY_ID="2345234jk2lh4234" - export AWS_SECRET_ACCESS_KEY="234jhkg234h52kh4g5khg34" - export TESTNET_NAME="remotenet" - export CLUSTER_NAME= "remotenetvalidators" - export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" - export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" - -These will be used by both ``terraform`` and ``ansible``. - -Create a remote network ------------------------ - -:: - - SERVERS=1 REGION_LIMIT=1 make validators-start - - -The testnet name is what's going to be used in --chain-id, while the cluster name is the administrative tag in AWS for the servers. The code will create SERVERS amount of servers in each availability zone up to the number of REGION_LIMITs, starting at us-east-2. (us-east-1 is excluded.) The below BaSH script does the same, but sometimes it's more comfortable for input. - -:: - - ./new-testnet.sh "$TESTNET_NAME" "$CLUSTER_NAME" 1 1 - - -Quickly see the /status endpoint --------------------------------- - -:: - - make validators-status - - -Delete servers --------------- - -:: - - make validators-stop - -Logging -------- - -You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on `this page <https://app.logz.io/#/dashboard/data-sources/Filebeat>`__, then: - -:: - - yum install systemd-devel || echo "This will only work on RHEL-based systems." - apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." - - go get github.com/mheese/journalbeat - ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 - - -Monitoring ----------- - -You can install DataDog agent using - -:: - - make datadog-install - - diff --git a/networks/local/README.md b/networks/local/README.md index d38e837a2..3a0f855be 100644 --- a/networks/local/README.md +++ b/networks/local/README.md @@ -31,9 +31,9 @@ To start a 4 node testnet run: make localnet-start ``` -The nodes bind their RPC servers to ports 46657, 46660, 46662, and 46664 on the host. +The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the host. This file creates a 4-node network using the gaiadnode image. -The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 46656-46657, 46659-46660, 46661-46662, and 46663-46664 respectively. +The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively. To update the binary, just rebuild it and restart the nodes: diff --git a/networks/local/gaiadnode/Dockerfile b/networks/local/gaiadnode/Dockerfile index fc2c0d4a0..d82036a46 100644 --- a/networks/local/gaiadnode/Dockerfile +++ b/networks/local/gaiadnode/Dockerfile @@ -7,7 +7,7 @@ RUN apk update && \ VOLUME [ /gaiad ] WORKDIR /gaiad -EXPOSE 46656 46657 +EXPOSE 26656 26657 ENTRYPOINT ["/usr/bin/wrapper.sh"] CMD ["start"] STOPSIGNAL SIGTERM diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/network.d/conf.yaml b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/network.d/conf.yaml new file mode 100644 index 000000000..b174490fc --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/network.d/conf.yaml @@ -0,0 +1,9 @@ +init_config: + +instances: + - collect_connection_state: true + excluded_interfaces: + - lo + - lo0 + collect_rate_metrics: true + collect_count_metrics: true diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/process.d/conf.yaml b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/process.d/conf.yaml new file mode 100644 index 000000000..465cadad7 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/process.d/conf.yaml @@ -0,0 +1,15 @@ +init_config: + +instances: +- name: ssh + search_string: ['ssh', 'sshd'] + thresholds: + critical: [1, 5] +- name: gaiad + search_string: ['gaiad'] + thresholds: + critical: [1, 1] +- name: gaiacli + search_string: ['gaiacli'] + thresholds: + critical: [1, 1] diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/prometheus.d/conf.yaml b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/prometheus.d/conf.yaml new file mode 100644 index 000000000..b08908400 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/conf.d/prometheus.d/conf.yaml @@ -0,0 +1,7 @@ +init_config: + +instances: + - prometheus_url: http://127.0.0.1:26660 + namespace: "gaiad" + metrics: + - p2p: * diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/intake.logs.datadoghq.com.crt b/networks/remote/ansible/roles/install-datadog-agent/files/intake.logs.datadoghq.com.crt new file mode 100644 index 000000000..ef6d9b2c2 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/intake.logs.datadoghq.com.crt @@ -0,0 +1,78 @@ +-----BEGIN CERTIFICATE----- +MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB +IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ +cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5 +blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm +B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw +0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG +KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG +AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW +dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH +AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy +dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy +dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js +LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow +CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1 +59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t +6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI +8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1 +upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS +yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF +ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj +b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x +OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1 +dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW +gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH +MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH +MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy +MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0 +LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF +AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW +MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma +eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK +bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN +0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U +akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV +BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw +MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV +UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp +ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/ +y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N +Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo +Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C +zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J +Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB +AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O +BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV +rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u +c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud +HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG +BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G +VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1 +l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt +8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ +59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu +VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w= +-----END CERTIFICATE----- diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/logrotate.conf b/networks/remote/ansible/roles/install-datadog-agent/files/logrotate.conf new file mode 100644 index 000000000..e90a5ffb2 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/logrotate.conf @@ -0,0 +1,35 @@ +# see "man logrotate" for details +# rotate log files weekly +daily + +# keep 4 days worth of backlogs +rotate 4 + +# create new (empty) log files after rotating old ones +create + +# use date as a suffix of the rotated file +dateext + +# uncomment this if you want your log files compressed +compress + +# RPM packages drop log rotation information into this directory +include /etc/logrotate.d + +# no packages own wtmp and btmp -- we'll rotate them here +/var/log/wtmp { + monthly + create 0664 root utmp + minsize 1M + rotate 1 +} + +/var/log/btmp { + missingok + monthly + create 0600 root utmp + rotate 1 +} + +# system-specific logs may be also be configured here. diff --git a/networks/remote/ansible/roles/install-datadog-agent/files/syslog b/networks/remote/ansible/roles/install-datadog-agent/files/syslog new file mode 100644 index 000000000..8052df477 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/files/syslog @@ -0,0 +1,13 @@ +/var/log/cron +/var/log/maillog +/var/log/messages +/var/log/secure +/var/log/spooler +{ + missingok + sharedscripts + postrotate + /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true + service datadog-agent restart 2> /dev/null || true + endscript +} diff --git a/networks/remote/ansible/roles/install-datadog-agent/handlers/main.yml b/networks/remote/ansible/roles/install-datadog-agent/handlers/main.yml index 90e05c17d..04f72b74d 100644 --- a/networks/remote/ansible/roles/install-datadog-agent/handlers/main.yml +++ b/networks/remote/ansible/roles/install-datadog-agent/handlers/main.yml @@ -3,3 +3,8 @@ - name: restart datadog-agent service: name=datadog-agent state=restarted +- name: restart rsyslog + service: name=rsyslog state=restarted + +- name: restart journald + service: name=systemd-journald state=restarted diff --git a/networks/remote/ansible/roles/install-datadog-agent/tasks/main.yml b/networks/remote/ansible/roles/install-datadog-agent/tasks/main.yml index 4d5aa1877..bba86a5ac 100644 --- a/networks/remote/ansible/roles/install-datadog-agent/tasks/main.yml +++ b/networks/remote/ansible/roles/install-datadog-agent/tasks/main.yml @@ -13,3 +13,46 @@ DD_API_KEY: "{{DD_API_KEY}}" DD_HOST_TAGS: "testnet:{{TESTNET_NAME}},cluster:{{CLUSTER_NAME}}" +- name: Set datadog.yaml config + template: src=datadog.yaml.j2 dest=/etc/datadog-agent/datadog.yaml + notify: restart datadog-agent + +- name: Set metrics config + copy: src=conf.d/ dest=/etc/datadog-agent/conf.d/ + notify: restart datadog-agent + +- name: Disable journald rate-limiting + lineinfile: "dest=/etc/systemd/journald.conf regexp={{item.regexp}} line='{{item.line}}'" + with_items: + - { regexp: "^#RateLimitInterval", line: "RateLimitInterval=0s" } + - { regexp: "^#RateLimitBurst", line: "RateLimitBurst=0" } + - { regexp: "^#SystemMaxFileSize", line: "SystemMaxFileSize=500M" } + notify: restart journald + +- name: As long as Datadog does not support journald on RPM-based linux, we enable rsyslog + yum: "name={{item}} state=installed" + with_items: + - rsyslog + - rsyslog-gnutls + +#- name: Get DataDog certificate for rsyslog +# get_url: url=https://docs.datadoghq.com/crt/intake.logs.datadoghq.com.crt dest=/etc/ssl/certs/intake.logs.datadoghq.com.crt + +- name: Get DataDog certificate for rsyslog + copy: src=intake.logs.datadoghq.com.crt dest=/etc/ssl/certs/intake.logs.datadoghq.com.crt + +- name: Add datadog config to rsyslog + template: src=datadog.conf.j2 dest=/etc/rsyslog.d/datadog.conf mode=0600 + notify: restart rsyslog + +- name: Set logrotate to rotate daily so syslog does not use up all space + copy: src=logrotate.conf dest=/etc/logrotate.conf + +- name: Set syslog to restart datadog-agent after logrotate + copy: src=syslog dest=/etc/logrotate.d/syslog + +#semanage port -a -t syslog_tls_port_t -p tcp 10516 +- name: Enable rsyslog to report to port 10516 in SELinux + seport: ports=10516 proto=tcp reload=yes setype=syslog_tls_port_t state=present + notify: restart rsyslog + diff --git a/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.conf.j2 b/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.conf.j2 new file mode 100644 index 000000000..1ab7d1b07 --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.conf.j2 @@ -0,0 +1,14 @@ +$template DatadogFormat,"{{DD_API_KEY}} <%pri%>%protocol-version% %timestamp:::date-rfc3339% %HOSTNAME% %app-name% - - - %msg%\n" + +$imjournalRatelimitInterval 0 +$imjournalRatelimitBurst 0 + +$DefaultNetstreamDriver gtls +$DefaultNetstreamDriverCAFile /etc/ssl/certs/intake.logs.datadoghq.com.crt +$ActionSendStreamDriver gtls +$ActionSendStreamDriverMode 1 +$ActionSendStreamDriverAuthMode x509/name +$ActionSendStreamDriverPermittedPeer *.logs.datadoghq.com +*.* @@intake.logs.datadoghq.com:10516;DatadogFormat + + diff --git a/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.yaml.j2 b/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.yaml.j2 new file mode 100644 index 000000000..2f3eb286e --- /dev/null +++ b/networks/remote/ansible/roles/install-datadog-agent/templates/datadog.yaml.j2 @@ -0,0 +1,561 @@ + +# The host of the Datadog intake server to send Agent data to +dd_url: https://app.datadoghq.com + +# The Datadog api key to associate your Agent's data with your organization. +# Can be found here: +# https://app.datadoghq.com/account/settings +api_key: {{DD_API_KEY}} + +# If you need a proxy to connect to the Internet, provide it here (default: +# disabled). You can use the 'no_proxy' list to specify hosts that should +# bypass the proxy. These settings might impact your checks requests, please +# refer to the specific check documentation for more details. Environment +# variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (coma-separated string) will +# override the values set here. See https://docs.datadoghq.com/agent/proxy/. +# +# proxy: +# http: http(s)://user:password@proxy_for_http:port +# https: http(s)://user:password@proxy_for_https:port +# no_proxy: +# - host1 +# - host2 + +# Setting this option to "yes" will tell the agent to skip validation of SSL/TLS certificates. +# This may be necessary if the agent is running behind a proxy. See this page for details: +# https://github.com/DataDog/dd-agent/wiki/Proxy-Configuration#using-haproxy-as-a-proxy +# skip_ssl_validation: no + +# Setting this option to "yes" will force the agent to only use TLS 1.2 when +# pushing data to the url specified in "dd_url". +# force_tls_12: no + +# Force the hostname to whatever you want. (default: auto-detected) +# hostname: mymachine.mydomain + +# Make the agent use "hostname -f" on unix-based systems as a last resort +# way of determining the hostname instead of Golang "os.Hostname()" +# This will be enabled by default in version 6.4 +# More information at https://dtdg.co/flag-hostname-fqdn +# hostname_fqdn: false + +# Set the host's tags (optional) +tags: ['testnet:{{TESTNET_NAME}}','cluster:{{CLUSTER_NAME}}'] +# - mytag +# - env:prod +# - role:database + +# Histogram and Historate configuration +# +# Configure which aggregated value to compute. Possible values are: min, max, +# median, avg, sum and count. +# +# histogram_aggregates: ["max", "median", "avg", "count"] +# +# Configure which percentiles will be computed. Must be a list of float +# between 0 and 1. +# Warning: percentiles must be specified as yaml strings +# +# histogram_percentiles: ["0.95"] +# +# Copy histogram values to distributions for true global distributions (in beta) +# This will increase the number of custom metrics created +# histogram_copy_to_distribution: false +# +# A prefix to add to distribution metrics created when histogram_copy_to_distributions is true +# histogram_copy_to_distribution_prefix: "" + +# Forwarder timeout in seconds +# forwarder_timeout: 20 + +# The forwarder retries failed requests. Use this setting to change the +# maximum length of the forwarder's retry queue (each request in the queue +# takes no more than 2MB in memory) +# forwarder_retry_queue_max_size: 30 + +# The number of workers used by the forwarder. Please note each worker will +# open an outbound HTTP connection towards Datadog's metrics intake at every +# flush. +# forwarder_num_workers: 1 + +# Collect AWS EC2 custom tags as agent tags +collect_ec2_tags: true + +# The path containing check configuration files +# By default, uses the conf.d folder located in the agent configuration folder. +# confd_path: + +# Additional path where to search for Python checks +# By default, uses the checks.d folder located in the agent configuration folder. +# additional_checksd: + +# The port for the go_expvar server +# expvar_port: 5000 + +# The port on which the IPC api listens +# cmd_port: 5001 + +# The port for the browser GUI to be served +# Setting 'GUI_port: -1' turns off the GUI completely +# Default is '5002' on Windows and macOS ; turned off on Linux +# GUI_port: -1 + +# The Agent runs workers in parallel to execute checks. By default the number +# of workers is set to 1. If set to 0 the agent will automatically determine +# the best number of runners needed based on the number of checks running. This +# would optimize the check collection time but may produce CPU spikes. +# check_runners: 1 + +# Metadata collection should always be enabled, except if you are running several +# agents/dsd instances per host. In that case, only one agent should have it on. +# WARNING: disabling it on every agent will lead to display and billing issues +# enable_metadata_collection: true + +# Enable the gohai collection of systems data +# enable_gohai: true + +# IPC api server timeout in seconds +# server_timeout: 15 + +# Some environments may have the procfs file system mounted in a miscellaneous +# location. The procfs_path configuration parameter provides a mechanism to +# override the standard default location: '/proc' - this setting will trickle +# down to integrations and affect their behavior if they rely on the psutil +# python package. +# procfs_path: /proc + +# BETA: Encrypted Secrets (Linux only) +# +# This feature is in beta and its options or behaviour might break between +# minor or bugfix releases of the Agent. +# +# The agent can call an external command to fetch secrets. The command will be +# executed maximum once per instance containing an encrypted password. +# Secrets are cached by the agent, this will avoid executing again the +# secret_backend_command to fetch an already known secret (useful when combine +# with Autodiscovery). This feature is still in beta. +# +# For more information see: https://github.com/DataDog/datadog-agent/blob/master/docs/agent/secrets.md +# +# Path to the script to execute. The script must belong to the same user used +# to run the agent. Executable right must be given to the agent and no rights +# for 'group' or 'other'. +# secret_backend_command: /path/to/command +# +# A list of arguments to give to the command at each run (optional) +# secret_backend_arguments: +# - argument1 +# - argument2 +# +# The size in bytes of the buffer used to store the command answer (apply to +# both stdout and stderr) +# secret_backend_output_max_size: 1024 +# +# The timeout to execute the command in second +# secret_backend_timeout: 5 + + +# Metadata providers, add or remove from the list to enable or disable collection. +# Intervals are expressed in seconds. You can also set a provider's interval to 0 +# to disable it. +# metadata_providers: +# - name: k8s +# interval: 60 + +# DogStatsd +# +# If you don't want to enable the DogStatsd server, set this option to no +# use_dogstatsd: yes +# +# Make sure your client is sending to the same UDP port +# dogstatsd_port: 8125 +# +# The host to bind to receive external metrics (used only by the dogstatsd +# server for now). For dogstatsd this is ignored if +# 'dogstatsd_non_local_traffic' is set to true +# bind_host: localhost +# +# Dogstatsd can also listen for metrics on a Unix Socket (*nix only). +# Set to a valid filesystem path to enable. +# dogstatsd_socket: /var/run/dogstatsd/dsd.sock +# +# When using Unix Socket, dogstatsd can tag metrics with container metadata. +# If running dogstatsd in a container, host PID mode (e.g. with --pid=host) is required. +# dogstatsd_origin_detection: false +# +# The buffer size use to receive statsd packet, in bytes +# dogstatsd_buffer_size: 1024 +# +# Whether dogstatsd should listen to non local UDP traffic +# dogstatsd_non_local_traffic: no +# +# Publish dogstatsd's internal stats as Go expvars +# dogstatsd_stats_enable: no +# +# How many items in the dogstatsd's stats circular buffer +# dogstatsd_stats_buffer: 10 +# +# The port for the go_expvar server +# dogstatsd_stats_port: 5000 +# +# The number of bytes allocated to dogstatsd's socket receive buffer (POSIX +# system only). By default, this value is set by the system. If you need to +# increase the size of this buffer but keep the OS default value the same, you +# can set dogstatsd's receive buffer size here. The maximum accepted value +# might change depending on the OS. +# dogstatsd_so_rcvbuf: +# +# If you want to forward every packet received by the dogstatsd server +# to another statsd server, uncomment these lines. +# WARNING: Make sure that forwarded packets are regular statsd packets and not "dogstatsd" packets, +# as your other statsd server might not be able to handle them. +# statsd_forward_host: address_of_own_statsd_server +# statsd_forward_port: 8125 +# +# If you want all statsd metrics coming from this host to be namespaced +# you can configure the namspace below. Each metric received will be prefixed +# with the namespace before it's sent to Datadog. +# statsd_metric_namespace: + +# Logs agent +# +# Logs agent is disabled by default +logs_enabled: true +# +# Enable logs collection for all containers, disabled by default +# logs_config: +# container_collect_all: false +# + +# JMX +# +# jmx_pipe_path: +# jmx_pipe_name: dd-auto_discovery +# +# If you only run Autodiscovery tests, jmxfetch might fail to pick up custom_jar_paths +# set in the check templates. If that is the case, you can force custom jars here. +# jmx_custom_jars: +# - /jmx-jars/jboss-cli-client.jar +# +# When running in a memory cgroup, openjdk 8u131 and higher can automatically adjust +# its heap memory usage in accordance to the cgroup/container's memory limit. +# Default is false: we'll set a Xmx of 200MB if none is configured. +# Note: older openjdk versions and other jvms might fail to start if this option is set +# +# jmx_use_cgroup_memory_limit: true +# + +# Autoconfig +# +# Directory containing configuration templates +# autoconf_template_dir: /datadog/check_configs +# +# The providers the Agent should call to collect checks configurations. +# Please note the File Configuration Provider is enabled by default and cannot +# be configured. +# config_providers: + +## The kubelet provider handles templates embedded in pod annotations, see +## https://docs.datadoghq.com/guides/autodiscovery/#template-source-kubernetes-pod-annotations +# - name: kubelet +# polling: true + +## The docker provider handles templates embedded in container labels, see +## https://docs.datadoghq.com/guides/autodiscovery/#template-source-docker-label-annotations +# - name: docker +# polling: true + +# - name: etcd +# polling: true +# template_dir: /datadog/check_configs +# template_url: http://127.0.0.1 +# username: +# password: + +# - name: consul +# polling: true +# template_dir: /datadog/check_configs +# template_url: http://127.0.0.1 +# ca_file: +# ca_path: +# cert_file: +# key_file: +# username: +# password: +# token: + +# - name: zookeeper +# polling: true +# template_dir: /datadog/check_configs +# template_url: 127.0.0.1 +# username: +# password: + +# Logging +# +# log_level: info +# log_file: /var/log/datadog/agent.log + +# Set to 'yes' to output logs in JSON format +# log_format_json: no + +# Set to 'no' to disable logging to stdout +# log_to_console: yes + +# Set to 'yes' to disable logging to the log file +# disable_file_logging: no + +# Set to 'yes' to enable logging to syslog. +# +# log_to_syslog: no +# +# If 'syslog_uri' is left undefined/empty, a local domain socket connection will be attempted +# +# syslog_uri: +# +# Set to 'yes' to output in an RFC 5424-compliant format +# +# syslog_rfc: no +# +# If TLS enabled, you must specify a path to a PEM certificate here +# +# syslog_pem: /path/to/certificate.pem +# +# If TLS enabled, you must specify a path to a private key here +# +# syslog_key: /path/to/key.pem +# +# If TLS enabled, you may enforce TLS verification here (defaults to true) +# +# syslog_tls_verify: yes +# + +# Autodiscovery +# +# Change the root directory to look at to get cgroup statistics. Useful when running inside a +# container with host directories mounted on a different folder. +# Default if environment variable "DOCKER_DD_AGENT" is set to "yes" +# "/host/sys/fs/cgroup" and "/sys/fs/cgroup" if not. +# +# container_cgroup_root: /host/sys/fs/cgroup/ +# +# Change the root directory to look at to get proc statistics. Useful when running inside a +# container with host directories mounted on a different folder. +# Default if environment variable "DOCKER_DD_AGENT" is set to "yes" +# "/host/proc" and "/proc" if not. +# +# container_proc_root: /host/proc +# +# Choose "auto" if you want to let the agent find any relevant listener on your host +# At the moment, the only auto listener supported is docker +# If you have already set docker anywhere in the listeners, the auto listener is ignored +# listeners: +# - name: auto +# - name: docker +# +# Exclude containers from metrics and AD based on their name or image: +# An excluded container will not get any individual container metric reported for it. +# Please note that the `docker.containers.running`, `.stopped`, `.running.total` and +# `.stopped.total` metrics are not affected by these settings and always count all +# containers. This does not affect your per-container billing. +# +# How it works: include first. +# If a container matches an exclude rule, it won't be included unless it first matches an include rule. +# +# Rules are regexp. +# +# Examples: +# exclude all, except containers based on the 'ubuntu' image or the 'debian' image. +# ac_exclude: ["image:.*"] +# ac_include: ["image:ubuntu", "image:debian"] +# +# include all, except containers based on the 'ubuntu' image. +# ac_exclude: ["image:ubuntu"] +# ac_include: [] +# +# exclude all debian images except containers with a name starting with 'frontend'. +# ac_exclude: ["image:debian"] +# ac_include: ["name:frontend.*"] +# +# ac_exclude: [] +# ac_include: [] +# +# +# Exclude default pause containers from orchestrators. +# +# By default the agent will not monitor kubernetes/openshift pause +# container. They will still be counted in the container count (just like +# excluded containers) since ignoring them would give a wrong impression +# about the docker daemon load. +# +# exclude_pause_container: true + +# Exclude default containers from DockerCloud: +# The following configuration will instruct the agent to ignore the containers from Docker Cloud. +# You can remove the ones you want to collect. +# ac_exclude: ["image:dockercloud/network-daemon","image:dockercloud/cleanup","image:dockercloud/logrotate","image:dockercloud/events","image:dockercloud/ntpd"] +# ac_include: [] +# +# You can also use the regex to ignore them all: +# ac_exclude: ["image:dockercloud/*"] +# ac_include: [] +# +# The default timeout value when connecting to the docker daemon +# is 5 seconds. It can be configured with this option. +# docker_query_timeout: 5 +# + +# Docker tag extraction +# +# We can extract container label or environment variables +# as metric tags. If you prefix your tag name with +, it +# will only be added to high cardinality metrics (docker check) +# +# docker_labels_as_tags: +# label_name: tag_name +# high_cardinality_label_name: +tag_name +# docker_env_as_tags: +# ENVVAR_NAME: tag_name +# +# Example: +# docker_labels_as_tags: +# com.docker.compose.service: service_name +# com.docker.compose.project: +project_name +# + +# Kubernetes tag extraction +# +# We can extract pod labels and annotations as metric tags. If you prefix your +# tag name with +, it will only be added to high cardinality metrics +# +# kubernetes_pod_labels_as_tags: +# app: kube_app +# pod-template-hash: +kube_pod-template-hash +# +# kubernetes_pod_annotations_as_tags: +# app: kube_app +# pod-template-hash: +kube_pod-template-hash +# + +# ECS integration +# +# URL where the ECS agent can be found. Standard cases will be autodetected. +# ecs_agent_url: http://localhost:51678 +# + +# Kubernetes kubelet connectivity +# +# The kubelet host and port should be autodetected when running inside a pod. +# If you run into connectivity issues, you can set these options according to +# your cluster setup: +# kubernetes_kubelet_host: autodetected +# kubernetes_http_kubelet_port: 10255 +# kubernetes_https_kubelet_port: 10250 +# +# When using HTTPS, we verify the kubelet's certificate, you can tune this: +# kubelet_tls_verify: true +# kubelet_client_ca: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt +# +# If authentication is needed, the agent will use the pod's serviceaccount's +# credentials. If you want to use a different account, or are running the agent +# on the host, you can set the credentials to use here: +# kubelet_auth_token_path: /path/to/file +# kubelet_client_crt: /path/to/key +# kubelet_client_key: /path/to/key +# + +# Kubernetes apiserver integration +# +# When running in a pod, the agent will automatically use the pod's serviceaccount +# to authenticate with the apiserver. If you wish to install the agent out of a pod +# or customise connection parameters, you can provide the path to a KubeConfig file +# see https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/ +# +# kubernetes_kubeconfig_path: /path/to/file +# +# In order to collect Kubernetes service names, the agent needs certain rights (see RBAC documentation in +# [docker readme](https://github.com/DataDog/datadog-agent/blob/master/Dockerfiles/agent/README.md#kubernetes)). +# You can disable this option or set how often (in seconds) the agent refreshes the internal mapping of services to +# ContainerIDs with the following options: +# kubernetes_collect_metadata_tags: true +# kubernetes_metadata_tag_update_freq: 60 +# kubernetes_apiserver_client_timeout: 10 +# kubernetes_apiserver_poll_freq: 30 +# +# To collect Kubernetes events, leader election must be enabled and collect_kubernetes_events set to true. +# Only the leader will collect events. More details about events [here](https://github.com/DataDog/datadog-agent/blob/master/Dockerfilesagent/README.md#event-collection). +# collect_kubernetes_events: false +# +# +# Leader Election settings, more details about leader election [here](https://github.com/DataDog/datadog-agent/blob/master/Dockerfilesagent/README.md#leader-election) +# To enable the leader election on this node, set the leader_election variable to true. +# leader_election: false +# The leader election lease is an integer in seconds. +# leader_lease_duration: 60 +# +# Node labels that should be collected and their name in host tags. Off by default. +# Some of these labels are redundant with metadata collected by +# cloud provider crawlers (AWS, GCE, Azure) +# +# kubernetes_node_labels_as_tags: +# kubernetes.io/hostname: nodename +# beta.kubernetes.io/os: os + +# Process agent specific settings +# +process_config: +# A string indicating the enabled state of the Process Agent. +# If "false" (the default) it will only collect containers. +# If "true" it will collect containers and processes. +# If "disabled" it will be disabled altogether and won't start. + enabled: "true" +# The full path to the file where process-agent logs will be written. +# log_file: +# The interval, in seconds, at which we will run each check. If you want consistent +# behavior between real-time you may set the Container/ProcessRT intervals to 10. +# Defaults to 10s for normal checks and 2s for others. +# intervals: +# container: +# container_realtime: +# process: +# process_realtime: +# A list of regex patterns that will exclude a process if matched. +# blacklist_patterns: +# How many check results to buffer in memory when POST fails. The default is usually fine. +# queue_size: +# The maximum number of file descriptors to open when collecting net connections. +# Only change if you are running out of file descriptors from the Agent. +# max_proc_fds: +# The maximum number of processes or containers per message. +# Only change if the defaults are causing issues. +# max_per_message: +# Overrides the path to the Agent bin used for getting the hostname. The default is usually fine. +# dd_agent_bin: +# Overrides of the environment we pass to fetch the hostname. The default is usually fine. +# dd_agent_env: + +# Trace Agent Specific Settings +# +# apm_config: +# Whether or not the APM Agent should run +# enabled: true +# The environment tag that Traces should be tagged with +# Will inherit from "env" tag if none is applied here +# env: none +# The port that the Receiver should listen on +# receiver_port: 8126 +# Whether the Trace Agent should listen for non local traffic +# Only enable if Traces are being sent to this Agent from another host/container +# apm_non_local_traffic: false +# Extra global sample rate to apply on all the traces +# This sample rate is combined to the sample rate from the sampler logic, still promoting interesting traces +# From 1 (no extra rate) to 0 (don't sample at all) +# extra_sample_rate: 1.0 +# Maximum number of traces per second to sample. +# The limit is applied over an average over a few minutes ; much bigger spikes are possible. +# Set to 0 to disable the limit. +# max_traces_per_second: 10 +# A blacklist of regular expressions can be provided to disable certain traces based on their resource name +# all entries must be surrounded by double quotes and separated by commas +# Example: ["(GET|POST) /healthcheck", "GET /V1"] +# ignore_resources: [] diff --git a/networks/remote/ansible/roles/setup-journald/handlers/main.yml b/networks/remote/ansible/roles/setup-journald/handlers/main.yml index d26e03029..14f3b3376 100644 --- a/networks/remote/ansible/roles/setup-journald/handlers/main.yml +++ b/networks/remote/ansible/roles/setup-journald/handlers/main.yml @@ -3,4 +3,3 @@ - name: restart journald service: name=systemd-journald state=restarted - diff --git a/networks/remote/ansible/roles/setup-journald/tasks/main.yml b/networks/remote/ansible/roles/setup-journald/tasks/main.yml index 4b1b9121e..a7a105bf3 100644 --- a/networks/remote/ansible/roles/setup-journald/tasks/main.yml +++ b/networks/remote/ansible/roles/setup-journald/tasks/main.yml @@ -15,6 +15,3 @@ command: "systemd-tmpfiles --create --prefix /var/log/journal" notify: restart journald -#- name: Ability to get the core dump on SIGABRT -# shell: "ulimit -c unlimited" - diff --git a/networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml b/networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml index 03bdf40e0..ad705ada5 100644 --- a/networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml +++ b/networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml @@ -24,6 +24,6 @@ - name: Reset network when: UNSAFE_RESET_ALL | default(false) | bool - command: "sudo -u gaiad gaiad unsafe_reset_all" + command: "sudo -u gaiad gaiad unsafe-reset-all" notify: restart gaiad diff --git a/networks/remote/ansible/set-corefilesize.yml b/networks/remote/ansible/set-corefilesize.yml new file mode 100644 index 000000000..ae0f85291 --- /dev/null +++ b/networks/remote/ansible/set-corefilesize.yml @@ -0,0 +1,13 @@ +--- + +# Set the core file size to unlimited to allow the system to generate core dumps + +- hosts: all + any_errors_fatal: true + gather_facts: no + + tasks: + + - name: Set core file size to unlimited to be able to get the core dump on SIGABRT + shell: "ulimit -c unlimited" + diff --git a/networks/remote/ansible/setup-journald.yml b/networks/remote/ansible/setup-journald.yml index fc8a8b16c..369c483f3 100644 --- a/networks/remote/ansible/setup-journald.yml +++ b/networks/remote/ansible/setup-journald.yml @@ -1,5 +1,7 @@ --- +#DD_API_KEY + - hosts: all any_errors_fatal: true gather_facts: no diff --git a/networks/Makefile.do b/networks/remote/terraform-do/Makefile similarity index 99% rename from networks/Makefile.do rename to networks/remote/terraform-do/Makefile index a626bad8b..76040e208 100644 --- a/networks/Makefile.do +++ b/networks/remote/terraform-do/Makefile @@ -1,7 +1,6 @@ ######################################## ### WARNING: The DigitalOcean scripts are deprecated. They are still here because ### they might be useful for developers. -### Use -f to call this Makefile: "make -f Makefile.do target" # Name of the testnet. Used in chain-id. TESTNET_NAME?=remotenet diff --git a/networks/remote/terraform-do/README.md b/networks/remote/terraform-do/README.md new file mode 100644 index 000000000..0486a8bc4 --- /dev/null +++ b/networks/remote/terraform-do/README.md @@ -0,0 +1,58 @@ +# Terraform & Ansible + +WARNING: The Digital Ocean scripts are obsolete. They are here because they might still be useful for developers. + +Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on Digital Ocean then +[Ansible](http://www.ansible.com/) to create and manage testnets on those servers. + +## Prerequisites + +- Install [Terraform](https://www.terraform.io/downloads.html) and [Ansible](http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) on a Linux machine. +- Create a [DigitalOcean API token](https://cloud.digitalocean.com/settings/api/tokens) with read and write capability. +- Install the python dopy package (`pip install dopy`) (This is necessary for the digitalocean.py script for ansible.) +- Create SSH keys + +``` +export DO_API_TOKEN="abcdef01234567890abcdef01234567890" +export TESTNET_NAME="remotenet" +export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" +export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" +``` + +These will be used by both `terraform` and `ansible`. + +## Create a remote network + +``` +make remotenet-start +``` + +Optionally, you can set the number of servers you want to launch and the name of the testnet (which defaults to remotenet): + +``` +TESTNET_NAME="mytestnet" SERVERS=7 make remotenet-start +``` + +## Quickly see the /status endpoint + +``` +make remotenet-status +``` + +## Delete servers + +``` +make remotenet-stop +``` + +## Logging + +You can ship logs to Logz.io, an Elastic stack (Elastic search, Logstash and Kibana) service provider. You can set up your nodes to log there automatically. Create an account and get your API key from the notes on [this page](https://app.logz.io/#/dashboard/data-sources/Filebeat), then: + +``` +yum install systemd-devel || echo "This will only work on RHEL-based systems." +apt-get install libsystemd-dev || echo "This will only work on Debian-based systems." + +go get github.com/mheese/journalbeat +ansible-playbook -i inventory/digital_ocean.py -l remotenet logzio.yml -e LOGZIO_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 +``` diff --git a/networks/remote/terraform-do/README.rst b/networks/remote/terraform-do/README.rst deleted file mode 100644 index fdccb1a6a..000000000 --- a/networks/remote/terraform-do/README.rst +++ /dev/null @@ -1,53 +0,0 @@ -Using Terraform -=============== - -This is a `Terraform <https://www.terraform.io/>`__ configuration that sets up DigitalOcean droplets. - -Prerequisites -------------- - -- Install `HashiCorp Terraform <https://www.terraform.io>`__ on a linux machine. -- Create a `DigitalOcean API token <https://cloud.digitalocean.com/settings/api/tokens>`__ with read and write capability. -- Create SSH keys - -Build ------ - -:: - - export DO_API_TOKEN="abcdef01234567890abcdef01234567890" - export TESTNET_NAME="remotenet" - export SSH_PUBLIC_FILE="$HOME/.ssh/id_rsa.pub" - export SSH_PRIVATE_FILE="$HOME/.ssh/id_rsa" - - terraform init - terraform apply -var DO_API_TOKEN="$DO_API_TOKEN" -var SSH_PUBLIC_FILE="$SSH_PUBLIC_FILE" -var SSH_PRIVATE_FILE="$SSH_PRIVATE_FILE" - -At the end you will get a list of IP addresses that belongs to your new droplets. - -Destroy -------- - -Run the below: - -:: - - terraform destroy -var DO_API_TOKEN="$DO_API_TOKEN" -var SSH_PUBLIC_FILE="$SSH_PUBLIC_FILE" -var SSH_PRIVATE_FILE="$SSH_PRIVATE_FILE" - -Good to know ------------- - -The DigitalOcean API was not very reliable for me. If you find that terraform fails to install a specific server (for example cluster[2]), check -the regions variable and remove data center names that you find unreliable. The variable is at cluster/variables.tf - -Example: - -:: - - variable "regions" { - description = "Regions to launch in" - type = "list" - default = ["TOR1", "LON1"] - } - - diff --git a/server/start.go b/server/start.go index 64bd9fd45..8f369d517 100644 --- a/server/start.go +++ b/server/start.go @@ -101,7 +101,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { proxy.NewLocalClientCreator(app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, - node.DefaultMetricsProvider, + node.DefaultMetricsProvider(cfg.Instrumentation), ctx.Logger.With("module", "node"), ) if err != nil { diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 25d417a66..f852b4029 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -16,7 +16,7 @@ import ( // ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout func ShowNodeIDCmd(ctx *Context) *cobra.Command { return &cobra.Command{ - Use: "show_node_id", + Use: "show-node-id", Short: "Show this node's ID", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config @@ -34,7 +34,7 @@ func ShowNodeIDCmd(ctx *Context) *cobra.Command { func ShowValidatorCmd(ctx *Context) *cobra.Command { flagJSON := "json" cmd := cobra.Command{ - Use: "show_validator", + Use: "show-validator", Short: "Show this node's tendermint validator info", RunE: func(cmd *cobra.Command, args []string) error { @@ -68,7 +68,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { // UnsafeResetAllCmd - extension of the tendermint command, resets initialization func UnsafeResetAllCmd(ctx *Context) *cobra.Command { return &cobra.Command{ - Use: "unsafe_reset_all", + Use: "unsafe-reset-all", Short: "Reset blockchain database, priv_validator.json file, and the logger", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config diff --git a/server/util.go b/server/util.go index 51547d116..0f5a7b3eb 100644 --- a/server/util.go +++ b/server/util.go @@ -84,6 +84,7 @@ func interceptLoadConfig() (conf *cfg.Config, err error) { conf.ProfListenAddress = "localhost:6060" conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 + conf.TxIndex.IndexAllTags = true conf.Consensus.TimeoutCommit = 5000 cfg.WriteConfigFile(configFilePath, conf) // Fall through, just so that its parsed into memory. diff --git a/types/decimal.go b/types/decimal.go new file mode 100644 index 000000000..baf2d9573 --- /dev/null +++ b/types/decimal.go @@ -0,0 +1,421 @@ +package types + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" + "strings" + "testing" +) + +// NOTE: never use new(Dec) or else we will panic unmarshalling into the +// nil embedded big.Int +type Dec struct { + *big.Int `json:"int"` +} + +// number of decimal places +const ( + Precision = 10 + + // bytes required to represent the above precision + // ceil(log2(9999999999)) + DecimalPrecisionBits = 34 +) + +var ( + precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil) + fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2)) + precisionMultipliers []*big.Int + zeroInt = big.NewInt(0) + oneInt = big.NewInt(1) + tenInt = big.NewInt(10) +) + +// Set precision multipliers +func init() { + precisionMultipliers = make([]*big.Int, Precision+1) + for i := 0; i <= Precision; i++ { + precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) + } +} + +func precisionInt() *big.Int { + return new(big.Int).Set(precisionReuse) +} + +// nolint - common values +func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} } +func OneDec() Dec { return Dec{precisionInt()} } + +// calculate the precision multiplier +func calcPrecisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + zerosToAdd := Precision - prec + multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil) + return multiplier +} + +// get the precision multiplier, do not mutate result +func precisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + return precisionMultipliers[prec] +} + +//______________________________________________________________________________________________ + +// create a new Dec from integer assuming whole number +func NewDec(i int64) Dec { + return NewDecWithPrec(i, 0) +} + +// create a new Dec from integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecWithPrec(i, prec int64) Dec { + return Dec{ + new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)), + } +} + +// create a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigInt(i *big.Int) Dec { + return NewDecFromBigIntWithPrec(i, 0) +} + +// create a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigIntWithPrec(i *big.Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// create a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromInt(i Int) Dec { + return NewDecFromIntWithPrec(i, 0) +} + +// create a new Dec from big integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecFromIntWithPrec(i Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i.BigInt(), precisionMultiplier(prec)), + } +} + +// create a decimal from an input decimal string. +// valid must come in the form: +// (-) whole integers (.) decimal integers +// examples of acceptable input include: +// -123.456 +// 456.7890 +// 345 +// -456789 +// +// NOTE - An error will return if more decimal places +// are provided in the string than the constant Precision. +// +// CONTRACT - This function does not mutate the input str. +func NewDecFromStr(str string) (d Dec, err Error) { + if len(str) == 0 { + return d, ErrUnknownRequest("decimal string is empty") + } + + // first extract any negative symbol + neg := false + if str[0] == '-' { + neg = true + str = str[1:] + } + + if len(str) == 0 { + return d, ErrUnknownRequest("decimal string is empty") + } + + strs := strings.Split(str, ".") + lenDecs := 0 + combinedStr := strs[0] + if len(strs) == 2 { + lenDecs = len(strs[1]) + if lenDecs == 0 || len(combinedStr) == 0 { + return d, ErrUnknownRequest("bad decimal length") + } + combinedStr = combinedStr + strs[1] + } else if len(strs) > 2 { + return d, ErrUnknownRequest("too many periods to be a decimal string") + } + + if lenDecs > Precision { + return d, ErrUnknownRequest( + fmt.Sprintf("too much precision, maximum %v, len decimal %v", Precision, lenDecs)) + } + + // add some extra zero's to correct to the Precision factor + zerosToAdd := Precision - lenDecs + zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") + combinedStr = combinedStr + zeros + + combined, ok := new(big.Int).SetString(combinedStr, 10) + if !ok { + return d, ErrUnknownRequest(fmt.Sprintf("bad string to integer conversion, combinedStr: %v", combinedStr)) + } + if neg { + combined = new(big.Int).Neg(combined) + } + return Dec{combined}, nil +} + +//______________________________________________________________________________________________ +//nolint +func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // Is equal to zero +func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } +func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than +func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal +func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than +func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal +func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign + +// addition +func (d Dec) Add(d2 Dec) Dec { + res := new(big.Int).Add(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// subtraction +func (d Dec) Sub(d2 Dec) Dec { + res := new(big.Int).Sub(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// multiplication +func (d Dec) Mul(d2 Dec) Dec { + mul := new(big.Int).Mul(d.Int, d2.Int) + chopped := chopPrecisionAndRound(mul) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// quotient +func (d Dec) Quo(d2 Dec) Dec { + + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndRound(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +func (d Dec) String() string { + str := d.ToLeftPaddedWithDecimals(Precision) + placement := len(str) - Precision + if placement < 0 { + panic("too few decimal digits") + } + return str[:placement] + "." + str[placement:] +} + +// TODO panic if negative or if totalDigits < len(initStr)??? +// evaluate as an integer and return left padded string +func (d Dec) ToLeftPaddedWithDecimals(totalDigits int8) string { + intStr := d.Int.String() + fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` + return fmt.Sprintf(fcode, intStr) +} + +// TODO panic if negative or if totalDigits < len(initStr)??? +// evaluate as an integer and return left padded string +func (d Dec) ToLeftPadded(totalDigits int8) string { + chopped := chopPrecisionAndRoundNonMutative(d.Int) + intStr := chopped.String() + fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` + return fmt.Sprintf(fcode, intStr) +} + +// ____ +// __| |__ "chop 'em +// ` \ round!" +// ___|| ~ _ -bankers +// | | __ +// | | | __|__|__ +// |_____: / | $$$ | +// |________| + +// nolint - go-cyclo +// Remove a Precision amount of rightmost digits and perform bankers rounding +// on the remainder (gaussian rounding) on the digits which have been removed. +// +// Mutates the input. Use the non-mutative version if that is undesired +func chopPrecisionAndRound(d *big.Int) *big.Int { + + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + d = chopPrecisionAndRound(d) + d = d.Neg(d) + return d + } + + // get the trucated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + switch rem.Cmp(fivePrecision) { + case -1: + return quo + case 1: + return quo.Add(quo, oneInt) + default: // bankers rounding must take place + // always round to an even number + if quo.Bit(0) == 0 { + return quo + } + return quo.Add(quo, oneInt) + } +} + +func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndRound(tmp) +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d Dec) RoundInt64() int64 { + chopped := chopPrecisionAndRoundNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +func (d Dec) RoundInt() Int { + return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.Int)) +} + +//___________________________________________________________________________________ + +// reuse nil values +var ( + nilAmino string + nilJSON []byte +) + +func init() { + empty := new(big.Int) + bz, err := empty.MarshalText() + if err != nil { + panic("bad nil amino init") + } + nilAmino = string(bz) + + nilJSON, err = json.Marshal(string(bz)) + if err != nil { + panic("bad nil json init") + } +} + +// wraps d.MarshalText() +func (d Dec) MarshalAmino() (string, error) { + if d.Int == nil { + return nilAmino, nil + } + bz, err := d.Int.MarshalText() + return string(bz), err +} + +// requires a valid JSON string - strings quotes and calls UnmarshalText +func (d *Dec) UnmarshalAmino(text string) (err error) { + tempInt := new(big.Int) + err = tempInt.UnmarshalText([]byte(text)) + if err != nil { + return err + } + d.Int = tempInt + return nil +} + +// MarshalJSON defines custom encoding scheme +func (d Dec) MarshalJSON() ([]byte, error) { + if d.Int == nil { + return nilJSON, nil + } + + bz, err := d.Int.MarshalText() + if err != nil { + return nil, err + } + return json.Marshal(string(bz)) +} + +// UnmarshalJSON defines custom decoding scheme +func (d *Dec) UnmarshalJSON(bz []byte) error { + if d.Int == nil { + d.Int = new(big.Int) + } + + var text string + err := json.Unmarshal(bz, &text) + if err != nil { + return err + } + return d.Int.UnmarshalText([]byte(text)) +} + +//___________________________________________________________________________________ +// helpers + +// test if two decimal arrays are equal +func DecsEqual(d1s, d2s []Dec) bool { + if len(d1s) != len(d2s) { + return false + } + + for i, d1 := range d1s { + if !d1.Equal(d2s[i]) { + return false + } + } + return true +} + +// minimum decimal between two +func MinDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d1 + } + return d2 +} + +// intended to be used with require/assert: require.True(DecEq(...)) +func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, Dec, Dec) { + return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got +} diff --git a/types/decimal_test.go b/types/decimal_test.go new file mode 100644 index 000000000..115eeacb0 --- /dev/null +++ b/types/decimal_test.go @@ -0,0 +1,301 @@ +package types + +import ( + "math/big" + "testing" + + wire "github.com/cosmos/cosmos-sdk/wire" + "github.com/stretchr/testify/require" +) + +// create a decimal from a decimal string (ex. "1234.5678") +func mustNewDecFromStr(t *testing.T, str string) (d Dec) { + d, err := NewDecFromStr(str) + require.NoError(t, err) + return d +} + +//_______________________________________ + +func TestPrecisionMultiplier(t *testing.T) { + res := precisionMultiplier(5) + exp := big.NewInt(100000) + require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) +} + +func TestNewDecFromStr(t *testing.T) { + largeBigInt, success := new(big.Int).SetString("3144605511029693144278234343371835", 10) + require.True(t, success) + tests := []struct { + decimalStr string + expErr bool + exp Dec + }{ + {"", true, Dec{}}, + {"0.-75", true, Dec{}}, + {"0", false, NewDec(0)}, + {"1", false, NewDec(1)}, + {"1.1", false, NewDecWithPrec(11, 1)}, + {"0.75", false, NewDecWithPrec(75, 2)}, + {"0.8", false, NewDecWithPrec(8, 1)}, + {"0.11111", false, NewDecWithPrec(11111, 5)}, + {"314460551102969.3144278234343371835", true, NewDec(3141203149163817869)}, + {"314460551102969314427823434337.1835718092488231350", + true, NewDecFromBigIntWithPrec(largeBigInt, 4)}, + {"314460551102969314427823434337.1835", + false, NewDecFromBigIntWithPrec(largeBigInt, 4)}, + {".", true, Dec{}}, + {".0", true, NewDec(0)}, + {"1.", true, NewDec(1)}, + {"foobar", true, Dec{}}, + {"0.foobar", true, Dec{}}, + {"0.foobar.", true, Dec{}}, + } + + for tcIndex, tc := range tests { + res, err := NewDecFromStr(tc.decimalStr) + if tc.expErr { + require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + } else { + require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + require.True(t, res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex) + } + + // negative tc + res, err = NewDecFromStr("-" + tc.decimalStr) + if tc.expErr { + require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + } else { + require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + exp := tc.exp.Mul(NewDec(-1)) + require.True(t, res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex) + } + } +} + +func TestEqualities(t *testing.T) { + tests := []struct { + d1, d2 Dec + gt, lt, eq bool + }{ + {NewDec(0), NewDec(0), false, false, true}, + {NewDecWithPrec(0, 2), NewDecWithPrec(0, 4), false, false, true}, + {NewDecWithPrec(100, 0), NewDecWithPrec(100, 0), false, false, true}, + {NewDecWithPrec(-100, 0), NewDecWithPrec(-100, 0), false, false, true}, + {NewDecWithPrec(-1, 1), NewDecWithPrec(-1, 1), false, false, true}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(3333, 3), false, false, true}, + + {NewDecWithPrec(0, 0), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(0, 0), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(-1, 0), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(-1, 0), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(1111, 3), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(1111, 3), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(-3333, 3), NewDecWithPrec(-1111, 3), false, true, false}, + + {NewDecWithPrec(3333, 3), NewDecWithPrec(0, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(0, 0), true, false, false}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(-1, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(-1, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(1111, 3), true, false, false}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(1111, 3), true, false, false}, + {NewDecWithPrec(-1111, 3), NewDecWithPrec(-3333, 3), true, false, false}, + } + + for tcIndex, tc := range tests { + require.Equal(t, tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex) + require.Equal(t, tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex) + require.Equal(t, tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex) + } + +} + +func TestDecsEqual(t *testing.T) { + tests := []struct { + d1s, d2s []Dec + eq bool + }{ + {[]Dec{NewDec(0)}, []Dec{NewDec(0)}, true}, + {[]Dec{NewDec(0)}, []Dec{NewDec(1)}, false}, + {[]Dec{NewDec(0)}, []Dec{}, false}, + {[]Dec{NewDec(0), NewDec(1)}, []Dec{NewDec(0), NewDec(1)}, true}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1), NewDec(0)}, true}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(0), NewDec(1)}, false}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1)}, false}, + {[]Dec{NewDec(1), NewDec(2)}, []Dec{NewDec(2), NewDec(4)}, false}, + {[]Dec{NewDec(3), NewDec(18)}, []Dec{NewDec(1), NewDec(6)}, false}, + } + + for tcIndex, tc := range tests { + require.Equal(t, tc.eq, DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex) + require.Equal(t, tc.eq, DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex) + } +} + +func TestArithmetic(t *testing.T) { + tests := []struct { + d1, d2 Dec + expMul, expDiv, expAdd, expSub Dec + }{ + // d1 d2 MUL DIV ADD SUB + {NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, + {NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, + {NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, + {NewDec(0), NewDec(-1), NewDec(0), NewDec(0), NewDec(-1), NewDec(1)}, + {NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(-1)}, + + {NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(2), NewDec(0)}, + {NewDec(-1), NewDec(-1), NewDec(1), NewDec(1), NewDec(-2), NewDec(0)}, + {NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, + {NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, + + {NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(4285714286, 10), NewDec(10), NewDec(-4)}, + {NewDec(2), NewDec(4), NewDec(8), NewDecWithPrec(5, 1), NewDec(6), NewDec(-2)}, + {NewDec(100), NewDec(100), NewDec(10000), NewDec(1), NewDec(200), NewDec(0)}, + + {NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), + NewDec(1), NewDec(3), NewDec(0)}, + {NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), + NewDecWithPrec(10009009009, 9), NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, + } + + for tcIndex, tc := range tests { + resAdd := tc.d1.Add(tc.d2) + resSub := tc.d1.Sub(tc.d2) + resMul := tc.d1.Mul(tc.d2) + require.True(t, tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex) + require.True(t, tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex) + require.True(t, tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex) + + if tc.d2.IsZero() { // panic for divide by zero + require.Panics(t, func() { tc.d1.Quo(tc.d2) }) + } else { + resDiv := tc.d1.Quo(tc.d2) + require.True(t, tc.expDiv.Equal(resDiv), "exp %v, res %v, tc %d", tc.expDiv.String(), resDiv.String(), tcIndex) + } + } +} + +func TestBankerRoundChop(t *testing.T) { + tests := []struct { + d1 Dec + exp int64 + }{ + {mustNewDecFromStr(t, "0.25"), 0}, + {mustNewDecFromStr(t, "0"), 0}, + {mustNewDecFromStr(t, "1"), 1}, + {mustNewDecFromStr(t, "0.75"), 1}, + {mustNewDecFromStr(t, "0.5"), 0}, + {mustNewDecFromStr(t, "7.5"), 8}, + {mustNewDecFromStr(t, "1.5"), 2}, + {mustNewDecFromStr(t, "2.5"), 2}, + {mustNewDecFromStr(t, "0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even + {mustNewDecFromStr(t, "1.545"), 2}, + } + + for tcIndex, tc := range tests { + resNeg := tc.d1.Neg().RoundInt64() + require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + + resPos := tc.d1.RoundInt64() + require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + } +} + +func TestToLeftPadded(t *testing.T) { + tests := []struct { + dec Dec + digits int8 + exp string + }{ + {mustNewDecFromStr(t, "33.3"), 8, "00000033"}, + {mustNewDecFromStr(t, "50"), 8, "00000050"}, + {mustNewDecFromStr(t, "333"), 8, "00000333"}, + {mustNewDecFromStr(t, "333"), 12, "000000000333"}, + {mustNewDecFromStr(t, "0.3333"), 8, "00000000"}, + } + for tcIndex, tc := range tests { + res := tc.dec.ToLeftPadded(tc.digits) + require.Equal(t, tc.exp, res, "incorrect left padding, tc %d", tcIndex) + } +} + +var cdc = wire.NewCodec() + +func TestZeroDeserializationJSON(t *testing.T) { + d := Dec{new(big.Int)} + err := cdc.UnmarshalJSON([]byte(`"0"`), &d) + require.Nil(t, err) + err = cdc.UnmarshalJSON([]byte(`"{}"`), &d) + require.NotNil(t, err) +} + +func TestSerializationText(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := d.MarshalText() + require.NoError(t, err) + + d2 := Dec{new(big.Int)} + err = d2.UnmarshalText(bz) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +func TestSerializationGoWireJSON(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := cdc.MarshalJSON(d) + require.NoError(t, err) + + d2 := Dec{new(big.Int)} + err = cdc.UnmarshalJSON(bz, &d2) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +func TestSerializationGoWireBinary(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := cdc.MarshalBinary(d) + require.NoError(t, err) + + var d2 Dec + err = cdc.UnmarshalBinary(bz, &d2) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +type testDEmbedStruct struct { + Field1 string `json:"f1"` + Field2 int `json:"f2"` + Field3 Dec `json:"f3"` +} + +// TODO make work for UnmarshalJSON +func TestEmbeddedStructSerializationGoWire(t *testing.T) { + obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} + bz, err := cdc.MarshalBinary(obj) + require.Nil(t, err) + + var obj2 testDEmbedStruct + err = cdc.UnmarshalBinary(bz, &obj2) + require.Nil(t, err) + + require.Equal(t, obj.Field1, obj2.Field1) + require.Equal(t, obj.Field2, obj2.Field2) + require.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2) +} + +func TestStringOverflow(t *testing.T) { + // two random 64 bit primes + dec1, err := NewDecFromStr("51643150036226787134389711697696177267") + require.NoError(t, err) + dec2, err := NewDecFromStr("-31798496660535729618459429845579852627") + require.NoError(t, err) + dec3 := dec1.Add(dec2) + require.Equal(t, + "19844653375691057515930281852116324640.0000000000", + dec3.String(), + ) +} diff --git a/types/rational.go b/types/rational.go deleted file mode 100644 index 89cc76968..000000000 --- a/types/rational.go +++ /dev/null @@ -1,262 +0,0 @@ -package types - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "testing" -) - -// "that's one big rat!" -// ______ -// / / /\ \____oo -// __ /___...._____ _\o -// __| |_ |_ - -// NOTE: never use new(Rat) or else -// we will panic unmarshalling into the -// nil embedded big.Rat -type Rat struct { - *big.Rat `json:"rat"` -} - -// nolint - common values -func ZeroRat() Rat { return Rat{big.NewRat(0, 1)} } -func OneRat() Rat { return Rat{big.NewRat(1, 1)} } - -// New - create a new Rat from integers -func NewRat(Numerator int64, Denominator ...int64) Rat { - switch len(Denominator) { - case 0: - return Rat{big.NewRat(Numerator, 1)} - case 1: - return Rat{big.NewRat(Numerator, Denominator[0])} - default: - panic("improper use of New, can only have one denominator") - } -} - -func getNumeratorDenominator(str []string, prec int) (numerator string, denom int64, err Error) { - switch len(str) { - case 1: - if len(str[0]) == 0 { - return "", 0, ErrUnknownRequest("not a decimal string") - } - numerator = str[0] - return numerator, 1, nil - case 2: - if len(str[0]) == 0 || len(str[1]) == 0 { - return "", 0, ErrUnknownRequest("not a decimal string") - } - if len(str[1]) > prec { - return "", 0, ErrUnknownRequest("string has too many decimals") - } - numerator = str[0] + str[1] - len := int64(len(str[1])) - denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64() - return numerator, denom, nil - default: - return "", 0, ErrUnknownRequest("not a decimal string") - } -} - -// create a rational from decimal string or integer string -// precision is the number of values after the decimal point which should be read -func NewRatFromDecimal(decimalStr string, prec int) (f Rat, err Error) { - // first extract any negative symbol - if len(decimalStr) == 0 { - return f, ErrUnknownRequest("decimal string is empty") - } - - neg := false - if string(decimalStr[0]) == "-" { - neg = true - decimalStr = decimalStr[1:] - } - - str := strings.Split(decimalStr, ".") - - numStr, denom, err := getNumeratorDenominator(str, prec) - if err != nil { - return f, err - } - - num, errConv := strconv.Atoi(numStr) - if errConv != nil && strings.HasSuffix(errConv.Error(), "value out of range") { - // resort to big int, don't make this default option for efficiency - numBig, success := new(big.Int).SetString(numStr, 10) - if success != true { - return f, ErrUnknownRequest("not a decimal string") - } - - if neg { - numBig.Neg(numBig) - } - - return NewRatFromBigInt(numBig, big.NewInt(denom)), nil - } else if errConv != nil { - return f, ErrUnknownRequest("not a decimal string") - } - - if neg { - num *= -1 - } - - return NewRat(int64(num), denom), nil -} - -// NewRatFromBigInt constructs Rat from big.Int -func NewRatFromBigInt(num *big.Int, denom ...*big.Int) Rat { - switch len(denom) { - case 0: - return Rat{new(big.Rat).SetInt(num)} - case 1: - return Rat{new(big.Rat).SetFrac(num, denom[0])} - default: - panic("improper use of NewRatFromBigInt, can only have one denominator") - } -} - -// NewRatFromInt constructs Rat from Int -func NewRatFromInt(num Int, denom ...Int) Rat { - switch len(denom) { - case 0: - return Rat{new(big.Rat).SetInt(num.BigInt())} - case 1: - return Rat{new(big.Rat).SetFrac(num.BigInt(), denom[0].BigInt())} - default: - panic("improper use of NewRatFromBigInt, can only have one denominator") - } -} - -//nolint -func (r Rat) Num() Int { return Int{r.Rat.Num()} } // Num - return the numerator -func (r Rat) Denom() Int { return Int{r.Rat.Denom()} } // Denom - return the denominator -func (r Rat) IsZero() bool { return r.Num().IsZero() } // IsZero - Is the Rat equal to zero -func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 } -func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than -func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal -func (r Rat) LT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == -1 } // less than -func (r Rat) LTE(r2 Rat) bool { return !r.GT(r2) } // less than or equal -func (r Rat) Mul(r2 Rat) Rat { return Rat{new(big.Rat).Mul(r.Rat, r2.Rat)} } // Mul - multiplication -func (r Rat) Quo(r2 Rat) Rat { return Rat{new(big.Rat).Quo(r.Rat, r2.Rat)} } // Quo - quotient -func (r Rat) Add(r2 Rat) Rat { return Rat{new(big.Rat).Add(r.Rat, r2.Rat)} } // Add - addition -func (r Rat) Sub(r2 Rat) Rat { return Rat{new(big.Rat).Sub(r.Rat, r2.Rat)} } // Sub - subtraction -func (r Rat) String() string { return r.Rat.String() } -func (r Rat) FloatString() string { return r.Rat.FloatString(10) } // a human-friendly string format. The last digit is rounded to nearest, with halves rounded away from zero. - -var ( - zero = big.NewInt(0) - one = big.NewInt(1) - two = big.NewInt(2) - five = big.NewInt(5) - nFive = big.NewInt(-5) - ten = big.NewInt(10) -) - -// evaluate the rational using bankers rounding -func (r Rat) EvaluateBig() *big.Int { - - num := r.Rat.Num() - denom := r.Rat.Denom() - - d, rem := new(big.Int), new(big.Int) - d.QuoRem(num, denom, rem) - if rem.Cmp(zero) == 0 { // is the remainder zero - return d - } - - // evaluate the remainder using bankers rounding - tenNum := new(big.Int).Mul(num, ten) - tenD := new(big.Int).Mul(d, ten) - remainderDigit := new(big.Int).Sub(new(big.Int).Quo(tenNum, denom), tenD) // get the first remainder digit - isFinalDigit := (new(big.Int).Rem(tenNum, denom).Cmp(zero) == 0) // is this the final digit in the remainder? - - switch { - case isFinalDigit && (remainderDigit.Cmp(five) == 0 || remainderDigit.Cmp(nFive) == 0): - dRem2 := new(big.Int).Rem(d, two) - return new(big.Int).Add(d, dRem2) // always rounds to the even number - case remainderDigit.Cmp(five) != -1: //remainderDigit >= 5: - d.Add(d, one) - case remainderDigit.Cmp(nFive) != 1: //remainderDigit <= -5: - d.Sub(d, one) - } - return d -} - -// RoundInt64 rounds the rational using bankers rounding -func (r Rat) RoundInt64() int64 { - return r.EvaluateBig().Int64() -} - -// RoundInt round the rational using bankers rounding -func (r Rat) RoundInt() Int { - return NewIntFromBigInt(r.EvaluateBig()) -} - -// round Rat with the provided precisionFactor -func (r Rat) Round(precisionFactor int64) Rat { - rTen := Rat{new(big.Rat).Mul(r.Rat, big.NewRat(precisionFactor, 1))} - return Rat{big.NewRat(rTen.RoundInt64(), precisionFactor)} -} - -// TODO panic if negative or if totalDigits < len(initStr)??? -// evaluate as an integer and return left padded string -func (r Rat) ToLeftPadded(totalDigits int8) string { - intStr := r.EvaluateBig().String() - fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` - return fmt.Sprintf(fcode, intStr) -} - -//___________________________________________________________________________________ - -//Wraps r.MarshalText(). -func (r Rat) MarshalAmino() (string, error) { - if r.Rat == nil { - r.Rat = new(big.Rat) - } - bz, err := r.Rat.MarshalText() - return string(bz), err -} - -// Requires a valid JSON string - strings quotes and calls UnmarshalText -func (r *Rat) UnmarshalAmino(text string) (err error) { - tempRat := big.NewRat(0, 1) - err = tempRat.UnmarshalText([]byte(text)) - if err != nil { - return err - } - r.Rat = tempRat - return nil -} - -//___________________________________________________________________________________ -// helpers - -// test if two rat arrays are equal -func RatsEqual(r1s, r2s []Rat) bool { - if len(r1s) != len(r2s) { - return false - } - - for i, r1 := range r1s { - if !r1.Equal(r2s[i]) { - return false - } - } - return true -} - -// intended to be used with require/assert: require.True(RatEq(...)) -func RatEq(t *testing.T, exp, got Rat) (*testing.T, bool, string, Rat, Rat) { - return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got -} - -// minimum rational between two -func MinRat(r1, r2 Rat) Rat { - if r1.LT(r2) { - return r1 - } - return r2 -} diff --git a/types/rational_test.go b/types/rational_test.go deleted file mode 100644 index 940de23dc..000000000 --- a/types/rational_test.go +++ /dev/null @@ -1,402 +0,0 @@ -package types - -import ( - "math/big" - "math/rand" - "testing" - - wire "github.com/cosmos/cosmos-sdk/wire" - "github.com/stretchr/testify/require" -) - -func TestNew(t *testing.T) { - require.Equal(t, NewRat(1), NewRat(1, 1)) - require.Equal(t, NewRat(100), NewRat(100, 1)) - require.Equal(t, NewRat(-1), NewRat(-1, 1)) - require.Equal(t, NewRat(-100), NewRat(-100, 1)) - require.Equal(t, NewRat(0), NewRat(0, 1)) - - // do not allow for more than 2 variables - require.Panics(t, func() { NewRat(1, 1, 1) }) -} - -func TestNewFromDecimal(t *testing.T) { - largeBigInt, success := new(big.Int).SetString("3109736052979742687701388262607869", 10) - require.True(t, success) - tests := []struct { - decimalStr string - expErr bool - exp Rat - }{ - {"", true, Rat{}}, - {"0", false, NewRat(0)}, - {"1", false, NewRat(1)}, - {"1.1", false, NewRat(11, 10)}, - {"0.75", false, NewRat(3, 4)}, - {"0.8", false, NewRat(4, 5)}, - {"0.11111", true, NewRat(1111, 10000)}, - {"628240629832763.5738930323617075341", true, NewRat(3141203149163817869, 5000)}, - {"621947210595948537540277652521.5738930323617075341", - true, NewRatFromBigInt(largeBigInt, big.NewInt(5000))}, - {"628240629832763.5738", false, NewRat(3141203149163817869, 5000)}, - {"621947210595948537540277652521.5738", - false, NewRatFromBigInt(largeBigInt, big.NewInt(5000))}, - {".", true, Rat{}}, - {".0", true, Rat{}}, - {"1.", true, Rat{}}, - {"foobar", true, Rat{}}, - {"0.foobar", true, Rat{}}, - {"0.foobar.", true, Rat{}}, - } - - for tcIndex, tc := range tests { - res, err := NewRatFromDecimal(tc.decimalStr, 4) - if tc.expErr { - require.NotNil(t, err, tc.decimalStr, "error expected, tc #%d", tcIndex) - } else { - require.Nil(t, err, tc.decimalStr, "unexpected error, tc #%d", tcIndex) - require.True(t, res.Equal(tc.exp), tc.decimalStr, "equality was incorrect, tc #%d", tcIndex) - } - - // negative tc - res, err = NewRatFromDecimal("-"+tc.decimalStr, 4) - if tc.expErr { - require.NotNil(t, err, tc.decimalStr, "error expected (negative case), tc #%d", tcIndex) - } else { - require.Nil(t, err, tc.decimalStr, "unexpected error (negative case), tc #%d", tcIndex) - require.True(t, res.Equal(tc.exp.Mul(NewRat(-1))), tc.decimalStr, "equality was incorrect (negative case), tc #%d", tcIndex) - } - } -} - -func TestEqualities(t *testing.T) { - tests := []struct { - r1, r2 Rat - gt, lt, eq bool - }{ - {NewRat(0), NewRat(0), false, false, true}, - {NewRat(0, 100), NewRat(0, 10000), false, false, true}, - {NewRat(100), NewRat(100), false, false, true}, - {NewRat(-100), NewRat(-100), false, false, true}, - {NewRat(-100, -1), NewRat(100), false, false, true}, - {NewRat(-1, 1), NewRat(1, -1), false, false, true}, - {NewRat(1, -1), NewRat(-1, 1), false, false, true}, - {NewRat(3, 7), NewRat(3, 7), false, false, true}, - - {NewRat(0), NewRat(3, 7), false, true, false}, - {NewRat(0), NewRat(100), false, true, false}, - {NewRat(-1), NewRat(3, 7), false, true, false}, - {NewRat(-1), NewRat(100), false, true, false}, - {NewRat(1, 7), NewRat(100), false, true, false}, - {NewRat(1, 7), NewRat(3, 7), false, true, false}, - {NewRat(-3, 7), NewRat(-1, 7), false, true, false}, - - {NewRat(3, 7), NewRat(0), true, false, false}, - {NewRat(100), NewRat(0), true, false, false}, - {NewRat(3, 7), NewRat(-1), true, false, false}, - {NewRat(100), NewRat(-1), true, false, false}, - {NewRat(100), NewRat(1, 7), true, false, false}, - {NewRat(3, 7), NewRat(1, 7), true, false, false}, - {NewRat(-1, 7), NewRat(-3, 7), true, false, false}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.gt, tc.r1.GT(tc.r2), "GT result is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.lt, tc.r1.LT(tc.r2), "LT result is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.eq, tc.r1.Equal(tc.r2), "equality result is incorrect, tc #%d", tcIndex) - } - -} - -func TestArithmetic(t *testing.T) { - tests := []struct { - r1, r2 Rat - resMul, resDiv, resAdd, resSub Rat - }{ - // r1 r2 MUL DIV ADD SUB - {NewRat(0), NewRat(0), NewRat(0), NewRat(0), NewRat(0), NewRat(0)}, - {NewRat(1), NewRat(0), NewRat(0), NewRat(0), NewRat(1), NewRat(1)}, - {NewRat(0), NewRat(1), NewRat(0), NewRat(0), NewRat(1), NewRat(-1)}, - {NewRat(0), NewRat(-1), NewRat(0), NewRat(0), NewRat(-1), NewRat(1)}, - {NewRat(-1), NewRat(0), NewRat(0), NewRat(0), NewRat(-1), NewRat(-1)}, - - {NewRat(1), NewRat(1), NewRat(1), NewRat(1), NewRat(2), NewRat(0)}, - {NewRat(-1), NewRat(-1), NewRat(1), NewRat(1), NewRat(-2), NewRat(0)}, - {NewRat(1), NewRat(-1), NewRat(-1), NewRat(-1), NewRat(0), NewRat(2)}, - {NewRat(-1), NewRat(1), NewRat(-1), NewRat(-1), NewRat(0), NewRat(-2)}, - - {NewRat(3), NewRat(7), NewRat(21), NewRat(3, 7), NewRat(10), NewRat(-4)}, - {NewRat(2), NewRat(4), NewRat(8), NewRat(1, 2), NewRat(6), NewRat(-2)}, - {NewRat(100), NewRat(100), NewRat(10000), NewRat(1), NewRat(200), NewRat(0)}, - - {NewRat(3, 2), NewRat(3, 2), NewRat(9, 4), NewRat(1), NewRat(3), NewRat(0)}, - {NewRat(3, 7), NewRat(7, 3), NewRat(1), NewRat(9, 49), NewRat(58, 21), NewRat(-40, 21)}, - {NewRat(1, 21), NewRat(11, 5), NewRat(11, 105), NewRat(5, 231), NewRat(236, 105), NewRat(-226, 105)}, - {NewRat(-21), NewRat(3, 7), NewRat(-9), NewRat(-49), NewRat(-144, 7), NewRat(-150, 7)}, - {NewRat(100), NewRat(1, 7), NewRat(100, 7), NewRat(700), NewRat(701, 7), NewRat(699, 7)}, - } - - for tcIndex, tc := range tests { - require.True(t, tc.resMul.Equal(tc.r1.Mul(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - require.True(t, tc.resAdd.Equal(tc.r1.Add(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - require.True(t, tc.resSub.Equal(tc.r1.Sub(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - - if tc.r2.Num().IsZero() { // panic for divide by zero - require.Panics(t, func() { tc.r1.Quo(tc.r2) }) - } else { - require.True(t, tc.resDiv.Equal(tc.r1.Quo(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - } - } -} - -func TestEvaluate(t *testing.T) { - tests := []struct { - r1 Rat - res int64 - }{ - {NewRat(0), 0}, - {NewRat(1), 1}, - {NewRat(1, 4), 0}, - {NewRat(1, 2), 0}, - {NewRat(3, 4), 1}, - {NewRat(5, 6), 1}, - {NewRat(3, 2), 2}, - {NewRat(5, 2), 2}, - {NewRat(6, 11), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even - {NewRat(17, 11), 2}, // 1.545 - {NewRat(5, 11), 0}, - {NewRat(16, 11), 1}, - {NewRat(113, 12), 9}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.r1.RoundInt64(), "%v. tc #%d", tc.r1, tcIndex) - require.Equal(t, tc.res*-1, tc.r1.Mul(NewRat(-1)).RoundInt64(), "%v. tc #%d", tc.r1.Mul(NewRat(-1)), tcIndex) - } -} - -func TestRound(t *testing.T) { - many3 := "333333333333333333333333333333333333333333333" - many7 := "777777777777777777777777777777777777777777777" - big3, worked := new(big.Int).SetString(many3, 10) - require.True(t, worked) - big7, worked := new(big.Int).SetString(many7, 10) - require.True(t, worked) - - tests := []struct { - r, res Rat - precFactor int64 - }{ - {NewRat(333, 777), NewRat(429, 1000), 1000}, - {Rat{new(big.Rat).SetFrac(big3, big7)}, NewRat(429, 1000), 1000}, - {Rat{new(big.Rat).SetFrac(big3, big7)}, Rat{big.NewRat(4285714286, 10000000000)}, 10000000000}, - {NewRat(1, 2), NewRat(1, 2), 1000}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.r.Round(tc.precFactor), "%v", tc.r, "incorrect rounding, tc #%d", tcIndex) - negR1, negRes := tc.r.Mul(NewRat(-1)), tc.res.Mul(NewRat(-1)) - require.Equal(t, negRes, negR1.Round(tc.precFactor), "%v", negR1, "incorrect rounding (negative case), tc #%d", tcIndex) - } -} - -func TestToLeftPadded(t *testing.T) { - tests := []struct { - rat Rat - digits int8 - res string - }{ - {NewRat(100, 3), 8, "00000033"}, - {NewRat(1, 3), 8, "00000000"}, - {NewRat(100, 2), 8, "00000050"}, - {NewRat(1000, 3), 8, "00000333"}, - {NewRat(1000, 3), 12, "000000000333"}, - } - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.rat.ToLeftPadded(tc.digits), "incorrect left padding, tc #%d", tcIndex) - } -} - -var cdc = wire.NewCodec() //var jsonCdc JSONCodec // TODO wire.Codec - -func TestZeroSerializationJSON(t *testing.T) { - r := NewRat(0, 1) - err := cdc.UnmarshalJSON([]byte(`"0/1"`), &r) - require.Nil(t, err) - err = cdc.UnmarshalJSON([]byte(`"0/0"`), &r) - require.NotNil(t, err) - err = cdc.UnmarshalJSON([]byte(`"1/0"`), &r) - require.NotNil(t, err) - err = cdc.UnmarshalJSON([]byte(`"{}"`), &r) - require.NotNil(t, err) -} - -func TestSerializationText(t *testing.T) { - r := NewRat(1, 3) - - bz, err := r.MarshalText() - require.NoError(t, err) - - var r2 = Rat{new(big.Rat)} - err = r2.UnmarshalText(bz) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -func TestSerializationGoWireJSON(t *testing.T) { - r := NewRat(1, 3) - bz, err := cdc.MarshalJSON(r) - require.NoError(t, err) - - var r2 Rat - err = cdc.UnmarshalJSON(bz, &r2) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -func TestSerializationGoWireBinary(t *testing.T) { - r := NewRat(1, 3) - bz, err := cdc.MarshalBinary(r) - require.NoError(t, err) - - var r2 Rat - err = cdc.UnmarshalBinary(bz, &r2) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -type testEmbedStruct struct { - Field1 string `json:"f1"` - Field2 int `json:"f2"` - Field3 Rat `json:"f3"` -} - -func TestEmbeddedStructSerializationGoWire(t *testing.T) { - obj := testEmbedStruct{"foo", 10, NewRat(1, 3)} - bz, err := cdc.MarshalJSON(obj) - require.Nil(t, err) - - var obj2 testEmbedStruct - err = cdc.UnmarshalJSON(bz, &obj2) - require.Nil(t, err) - - require.Equal(t, obj.Field1, obj2.Field1) - require.Equal(t, obj.Field2, obj2.Field2) - require.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2) -} - -func TestRatsEqual(t *testing.T) { - tests := []struct { - r1s, r2s []Rat - eq bool - }{ - {[]Rat{NewRat(0)}, []Rat{NewRat(0)}, true}, - {[]Rat{NewRat(0)}, []Rat{NewRat(1)}, false}, - {[]Rat{NewRat(0)}, []Rat{}, false}, - {[]Rat{NewRat(0), NewRat(1)}, []Rat{NewRat(0), NewRat(1)}, true}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1), NewRat(0)}, true}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(0), NewRat(1)}, false}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1)}, false}, - {[]Rat{NewRat(1), NewRat(2)}, []Rat{NewRat(2), NewRat(4)}, false}, - {[]Rat{NewRat(3), NewRat(18)}, []Rat{NewRat(1), NewRat(6)}, false}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.eq, RatsEqual(tc.r1s, tc.r2s), "equality of rational arrays is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.eq, RatsEqual(tc.r2s, tc.r1s), "equality of rational arrays is incorrect (converse), tc #%d", tcIndex) - } - -} - -func TestStringOverflow(t *testing.T) { - // two random 64 bit primes - rat1 := NewRat(5164315003622678713, 4389711697696177267) - rat2 := NewRat(-3179849666053572961, 8459429845579852627) - rat3 := rat1.Add(rat2) - require.Equal(t, - "29728537197630860939575850336935951464/37134458148982045574552091851127630409", - rat3.String(), - ) -} - -// Tests below uses randomness -// Since we are using *big.Rat as underlying value -// and (U/)Int is immutable value(see TestImmutability(U/)Int) -// it is safe to use randomness in the tests -func TestArithRat(t *testing.T) { - for i := 0; i < 20; i++ { - n1 := NewInt(int64(rand.Int31())) - d1 := NewInt(int64(rand.Int31())) - rat1 := NewRatFromInt(n1, d1) - - n2 := NewInt(int64(rand.Int31())) - d2 := NewInt(int64(rand.Int31())) - rat2 := NewRatFromInt(n2, d2) - - n1d2 := n1.Mul(d2) - n2d1 := n2.Mul(d1) - - cases := []struct { - nres Int - dres Int - rres Rat - }{ - {n1d2.Add(n2d1), d1.Mul(d2), rat1.Add(rat2)}, - {n1d2.Sub(n2d1), d1.Mul(d2), rat1.Sub(rat2)}, - {n1.Mul(n2), d1.Mul(d2), rat1.Mul(rat2)}, - {n1d2, n2d1, rat1.Quo(rat2)}, - } - - for _, tc := range cases { - require.Equal(t, NewRatFromInt(tc.nres, tc.dres), tc.rres) - } - } -} - -func TestCompRat(t *testing.T) { - for i := 0; i < 20; i++ { - n1 := NewInt(int64(rand.Int31())) - d1 := NewInt(int64(rand.Int31())) - rat1 := NewRatFromInt(n1, d1) - - n2 := NewInt(int64(rand.Int31())) - d2 := NewInt(int64(rand.Int31())) - rat2 := NewRatFromInt(n2, d2) - - n1d2 := n1.Mul(d2) - n2d1 := n2.Mul(d1) - - cases := []struct { - ires bool - rres bool - }{ - {n1d2.Equal(n2d1), rat1.Equal(rat2)}, - {n1d2.GT(n2d1), rat1.GT(rat2)}, - {n1d2.LT(n2d1), rat1.LT(rat2)}, - {n1d2.GT(n2d1) || n1d2.Equal(n2d1), rat1.GTE(rat2)}, - {n1d2.LT(n2d1) || n1d2.Equal(n2d1), rat1.LTE(rat2)}, - } - - for _, tc := range cases { - require.Equal(t, tc.ires, tc.rres) - } - } -} - -func TestImmutabilityRat(t *testing.T) { - for i := 0; i < 20; i++ { - n := int64(rand.Int31()) - r := NewRat(n) - z := ZeroRat() - o := OneRat() - - r.Add(z) - r.Sub(z) - r.Mul(o) - r.Quo(o) - - require.Equal(t, n, r.RoundInt64()) - require.True(t, NewRat(n).Equal(r)) - } - -} diff --git a/types/stake.go b/types/stake.go index 4e3cf38a3..acd24a154 100644 --- a/types/stake.go +++ b/types/stake.go @@ -42,9 +42,9 @@ type Validator interface { GetStatus() BondStatus // status of the validator GetOwner() AccAddress // owner AccAddress to receive/return validators coins GetPubKey() crypto.PubKey // validation pubkey - GetPower() Rat // validation power - GetTokens() Rat // validation tokens - GetDelegatorShares() Rat // Total out standing delegator shares + GetPower() Dec // validation power + GetTokens() Dec // validation tokens + GetDelegatorShares() Dec // Total out standing delegator shares GetBondHeight() int64 // height in which the validator became active } @@ -68,10 +68,10 @@ type ValidatorSet interface { Validator(Context, AccAddress) Validator // get a particular validator by owner AccAddress ValidatorByPubKey(Context, crypto.PubKey) Validator // get a particular validator by signing PubKey - TotalPower(Context) Rat // total power of the validator set + TotalPower(Context) Dec // total power of the validator set // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction - Slash(Context, crypto.PubKey, int64, int64, Rat) + Slash(Context, crypto.PubKey, int64, int64, Dec) Revoke(Context, crypto.PubKey) // revoke a validator Unrevoke(Context, crypto.PubKey) // unrevoke a validator } @@ -82,7 +82,7 @@ type ValidatorSet interface { type Delegation interface { GetDelegator() AccAddress // delegator AccAddress for the bond GetValidator() AccAddress // validator owner AccAddress for the bond - GetBondShares() Rat // amount of validator's shares + GetBondShares() Dec // amount of validator's shares } // properties for the set of all delegations for a particular diff --git a/version/version.go b/version/version.go index c6361dd50..9b332353b 100644 --- a/version/version.go +++ b/version/version.go @@ -2,10 +2,10 @@ package version const Maj = "0" -const Min = "22" -const Fix = "0" +const Min = "23" +const Fix = "1" -const Version = "0.22.0" +const Version = "0.23.1" // GitCommit set by build flags var GitCommit = "" diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index bebd24623..c6e280157 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -158,10 +158,10 @@ func (msg StdSignMsg) Bytes() []byte { // Standard Signature type StdSignature struct { - crypto.PubKey `json:"pub_key"` // optional - crypto.Signature `json:"signature"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + crypto.PubKey `json:"pub_key"` // optional + Signature []byte `json:"signature"` + AccountNumber int64 `json:"account_number"` + Sequence int64 `json:"sequence"` } // logic for standard transaction decoding diff --git a/x/distribution/keeper.go b/x/distribution/keeper.go index 145071719..aba07eaca 100644 --- a/x/distribution/keeper.go +++ b/x/distribution/keeper.go @@ -23,13 +23,13 @@ package stake ////_________________________________________________________________________ //// cummulative power of the non-absent prevotes -//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Rat { +//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Dec { //store := ctx.KVStore(k.storeKey) //// get absent prevote indexes //absents := ctx.AbsentValidators() -//TotalPower := sdk.ZeroRat() +//TotalPower := sdk.ZeroDec() //i := int32(0) //iterator := store.SubspaceIterator(ValidatorsBondedKey) //for ; iterator.Valid(); iterator.Next() { diff --git a/x/distribution/keeper_test.go b/x/distribution/keeper_test.go index 0d732f8a1..890268060 100644 --- a/x/distribution/keeper_test.go +++ b/x/distribution/keeper_test.go @@ -8,8 +8,8 @@ package stake //var candidatesIn [5]Candidate //for i, amt := range amts { //candidatesIn[i] = NewCandidate(addrVals[i], pks[i], Description{}) -//candidatesIn[i].BondedShares = sdk.NewRat(amt) -//candidatesIn[i].DelegatorShares = sdk.NewRat(amt) +//candidatesIn[i].BondedShares = sdk.NewDec(amt) +//candidatesIn[i].DelegatorShares = sdk.NewDec(amt) //keeper.setCandidate(ctx, candidatesIn[i]) //} @@ -18,7 +18,7 @@ package stake //require.Equal(t, 5, len(gotValidators)) //totPow := keeper.GetTotalPrecommitVotingPower(ctx) -//exp := sdk.NewRat(11111) +//exp := sdk.NewDec(11111) //require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) //// set absent gotValidators to be the 1st and 3rd record sorted by pubKey address @@ -26,6 +26,6 @@ package stake //totPow = keeper.GetTotalPrecommitVotingPower(ctx) //// XXX verify that this order should infact exclude these two records -//exp = sdk.NewRat(11100) +//exp = sdk.NewDec(11100) //require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) //} diff --git a/x/distribution/movement.go b/x/distribution/movement.go index 03c4de72c..399a25a68 100644 --- a/x/distribution/movement.go +++ b/x/distribution/movement.go @@ -17,7 +17,7 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //// calculate the proposer reward //precommitPower := k.GetTotalPrecommitVotingPower(ctx) -//toProposer := coinsMulRat(collectedFees, (sdk.NewRat(1, 100).Add(sdk.NewRat(4, 100).Mul(precommitPower).Quo(pool.BondedShares)))) +//toProposer := coinsMulRat(collectedFees, (sdk.NewDec(1, 100).Add(sdk.NewDec(4, 100).Mul(precommitPower).Quo(pool.BondedShares)))) //candidate.ProposerRewardPool = candidate.ProposerRewardPool.Plus(toProposer) //toReservePool := coinsMulRat(collectedFees, params.ReservePoolFee) @@ -34,10 +34,10 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //k.setPool(ctx, pool) //} -//func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins { +//func coinsMulRat(coins sdk.Coins, rat sdk.Dec) sdk.Coins { //var res sdk.Coins //for _, coin := range coins { -//coinMulAmt := rat.Mul(sdk.NewRat(coin.Amount)).Evaluate() +//coinMulAmt := rat.Mul(sdk.NewDec(coin.Amount)).Evaluate() //coinMul := sdk.Coins{{coin.Denom, coinMulAmt}} //res = res.Plus(coinMul) //} @@ -49,14 +49,14 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //// calculate adjustment changes for a candidate at a height //func CalculateAdjustmentChange(candidate Candidate, pool Pool, denoms []string, height int64) (Candidate, Pool) { -//heightRat := sdk.NewRat(height) -//lastHeightRat := sdk.NewRat(height - 1) +//heightRat := sdk.NewDec(height) +//lastHeightRat := sdk.NewDec(height - 1) //candidateFeeCount := candidate.BondedShares.Mul(heightRat) //poolFeeCount := pool.BondedShares.Mul(heightRat) //for i, denom := range denoms { -//poolFeeSumReceived := sdk.NewRat(pool.FeeSumReceived.AmountOf(denom)) -//poolFeeRecent := sdk.NewRat(pool.FeeRecent.AmountOf(denom)) +//poolFeeSumReceived := sdk.NewDec(pool.FeeSumReceived.AmountOf(denom)) +//poolFeeRecent := sdk.NewDec(pool.FeeRecent.AmountOf(denom)) //// calculate simple and projected pools //simplePool := candidateFeeCount.Quo(poolFeeCount).Mul(poolFeeSumReceived) //calc1 := candidate.PrevBondedShares.Mul(lastHeightRat).Quo(pool.PrevBondedShares.Mul(lastHeightRat)).Mul(poolFeeRecent) diff --git a/x/distribution/types.go b/x/distribution/types.go index f9d4f905f..223410471 100644 --- a/x/distribution/types.go +++ b/x/distribution/types.go @@ -23,8 +23,8 @@ package stake //// fee information for a validator //type Validator struct { -//Adjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools +//Adjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms +//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // total shares of a global hold pools //} ////_________________________________________________________________________ @@ -32,7 +32,7 @@ package stake //// Params defines the high level settings for staking //type Params struct { //FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms -//ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool +//ReservePoolFee sdk.Dec `json:"reserve_pool_fee"` // percent of fees which go to reserve pool //} //func (p Params) equal(p2 Params) bool { @@ -43,7 +43,7 @@ package stake //func defaultParams() Params { //return Params{ //FeeDenoms: []string{"steak"}, -//ReservePoolFee: sdk.NewRat(5, 100), +//ReservePoolFee: sdk.NewDec(5, 100), //} //} @@ -55,8 +55,8 @@ package stake //FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed //FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"` //FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected -//FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // XXX last recorded bonded shares +//FeeAdjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms +//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // XXX last recorded bonded shares //} //func (p Pool) equal(p2 Pool) bool { @@ -64,7 +64,7 @@ package stake //p.FeePool.IsEqual(p2.FeePool) && //p.FeeSumReceived.IsEqual(p2.FeeSumReceived) && //p.FeeRecent.IsEqual(p2.FeeRecent) && -//sdk.RatsEqual(p.FeeAdjustments, p2.FeeAdjustments) && +//sdk.DecsEqual(p.FeeAdjustments, p2.FeeAdjustments) && //p.PrevBondedShares.Equal(p2.PrevBondedShares) //} @@ -75,8 +75,8 @@ package stake //FeePool: sdk.Coins(nil), //FeeSumReceived: sdk.Coins(nil), //FeeRecent: sdk.Coins(nil), -//FeeAdjustments: []sdk.Rat{sdk.ZeroRat()}, -//PrevBondedShares: sdk.ZeroRat(), +//FeeAdjustments: []sdk.Dec{sdk.ZeroDec()}, +//PrevBondedShares: sdk.ZeroDec(), //} //} @@ -85,8 +85,8 @@ package stake //// Used in calculation of fee shares, added to a queue for each block where a power change occures //type PowerChange struct { //Height int64 `json:"height"` // block height at change -//Power sdk.Rat `json:"power"` // total power at change -//PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1 +//Power sdk.Dec `json:"power"` // total power at change +//PrevPower sdk.Dec `json:"prev_power"` // total power at previous height-1 //FeesIn sdk.Coins `json:"fees_in"` // fees in at block height //PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height //} diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 78b39ff1c..15f952c00 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -33,9 +33,9 @@ func DefaultGenesisState() GenesisState { VotingPeriod: 200, }, TallyingProcedure: TallyingProcedure{ - Threshold: sdk.NewRat(1, 2), - Veto: sdk.NewRat(1, 3), - GovernancePenalty: sdk.NewRat(1, 100), + Threshold: sdk.NewDecWithPrec(5, 1), + Veto: sdk.NewDecWithPrec(334, 3), + GovernancePenalty: sdk.NewDecWithPrec(1, 2), }, } } diff --git a/x/gov/procedures.go b/x/gov/procedures.go index f46c2149f..f74091c74 100644 --- a/x/gov/procedures.go +++ b/x/gov/procedures.go @@ -12,9 +12,9 @@ type DepositProcedure struct { // Procedure around Tallying votes in governance type TallyingProcedure struct { - Threshold sdk.Rat `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 - Veto sdk.Rat `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 - GovernancePenalty sdk.Rat `json:"governance_penalty"` // Penalty if validator does not vote + Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 + Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 + GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote } // Procedure around Voting in governance diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 01d839b8c..f05dabd08 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -174,11 +174,11 @@ func (pt *ProposalKind) UnmarshalJSON(data []byte) error { // Turns VoteOption byte to String func (pt ProposalKind) String() string { switch pt { - case 0x01: + case ProposalTypeText: return "Text" - case 0x02: + case ProposalTypeParameterChange: return "ParameterChange" - case 0x03: + case ProposalTypeSoftwareUpgrade: return "SoftwareUpgrade" default: return "" @@ -297,19 +297,19 @@ func (status ProposalStatus) Format(s fmt.State, verb rune) { //----------------------------------------------------------- // Tally Results type TallyResult struct { - Yes sdk.Rat `json:"yes"` - Abstain sdk.Rat `json:"abstain"` - No sdk.Rat `json:"no"` - NoWithVeto sdk.Rat `json:"no_with_veto"` + Yes sdk.Dec `json:"yes"` + Abstain sdk.Dec `json:"abstain"` + No sdk.Dec `json:"no"` + NoWithVeto sdk.Dec `json:"no_with_veto"` } // checks if two proposals are equal func EmptyTallyResult() TallyResult { return TallyResult{ - Yes: sdk.ZeroRat(), - Abstain: sdk.ZeroRat(), - No: sdk.ZeroRat(), - NoWithVeto: sdk.ZeroRat(), + Yes: sdk.ZeroDec(), + Abstain: sdk.ZeroDec(), + No: sdk.ZeroDec(), + NoWithVeto: sdk.ZeroDec(), } } diff --git a/x/gov/tally.go b/x/gov/tally.go index f8a341e1e..fc5b4317b 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -7,20 +7,20 @@ import ( // validatorGovInfo used for tallying type validatorGovInfo struct { Address sdk.AccAddress // sdk.AccAddress of the validator owner - Power sdk.Rat // Power of a Validator - DelegatorShares sdk.Rat // Total outstanding delegator shares - Minus sdk.Rat // Minus of validator, used to compute validator's voting power + Power sdk.Dec // Power of a Validator + DelegatorShares sdk.Dec // Total outstanding delegator shares + Minus sdk.Dec // Minus of validator, used to compute validator's voting power Vote VoteOption // Vote of the validator } func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult, nonVoting []sdk.AccAddress) { - results := make(map[VoteOption]sdk.Rat) - results[OptionYes] = sdk.ZeroRat() - results[OptionAbstain] = sdk.ZeroRat() - results[OptionNo] = sdk.ZeroRat() - results[OptionNoWithVeto] = sdk.ZeroRat() + results := make(map[VoteOption]sdk.Dec) + results[OptionYes] = sdk.ZeroDec() + results[OptionAbstain] = sdk.ZeroDec() + results[OptionNo] = sdk.ZeroDec() + results[OptionNoWithVeto] = sdk.ZeroDec() - totalVotingPower := sdk.ZeroRat() + totalVotingPower := sdk.ZeroDec() currValidators := make(map[string]validatorGovInfo) keeper.vs.IterateValidatorsBonded(ctx, func(index int64, validator sdk.Validator) (stop bool) { @@ -28,7 +28,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall Address: validator.GetOwner(), Power: validator.GetPower(), DelegatorShares: validator.GetDelegatorShares(), - Minus: sdk.ZeroRat(), + Minus: sdk.ZeroDec(), Vote: OptionEmpty, } return false @@ -91,7 +91,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall } // If no one votes, proposal fails - if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroRat()) { + if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) { return false, tallyResults, nonVoting } // If more than 1/3 of voters veto, proposal fails diff --git a/x/gov/test_common.go b/x/gov/test_common.go index df66fa40c..5e7977b50 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -64,7 +64,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk mapp.InitChainer(ctx, req) stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := stake.InitGenesis(ctx, stakeKeeper, stakeGenesis) if err != nil { diff --git a/x/params/keeper.go b/x/params/keeper.go index 8817b7c6c..69bcc05ea 100644 --- a/x/params/keeper.go +++ b/x/params/keeper.go @@ -183,8 +183,8 @@ func (k Getter) GetUint(ctx sdk.Context, key string) (res sdk.Uint, err error) { return } -// GetRat is helper function for rat params -func (k Getter) GetRat(ctx sdk.Context, key string) (res sdk.Rat, err error) { +// GetDec is helper function for decimal params +func (k Getter) GetDec(ctx sdk.Context, key string) (res sdk.Dec, err error) { store := ctx.KVStore(k.k.key) bz := store.Get([]byte(key)) err = k.k.cdc.UnmarshalBinary(bz, &res) @@ -301,8 +301,8 @@ func (k Getter) GetUintWithDefault(ctx sdk.Context, key string, def sdk.Uint) (r return } -// GetRatWithDefault is helper function for sdk.Rat params with default value -func (k Getter) GetRatWithDefault(ctx sdk.Context, key string, def sdk.Rat) (res sdk.Rat) { +// GetDecWithDefault is helper function for sdk.Dec params with default value +func (k Getter) GetDecWithDefault(ctx sdk.Context, key string, def sdk.Dec) (res sdk.Dec) { store := ctx.KVStore(k.k.key) bz := store.Get([]byte(key)) if bz == nil { @@ -397,8 +397,8 @@ func (k Setter) SetUint(ctx sdk.Context, key string, param sdk.Uint) { } } -// SetRat is helper function for rat params -func (k Setter) SetRat(ctx sdk.Context, key string, param sdk.Rat) { +// SetDec is helper function for decimal params +func (k Setter) SetDec(ctx sdk.Context, key string, param sdk.Dec) { if err := k.k.set(ctx, key, param); err != nil { panic(err) } diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index 4bb5744ea..626d68c7d 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -94,7 +94,7 @@ func TestGetter(t *testing.T) { {"uint64", uint64(1)}, {"int", sdk.NewInt(1)}, {"uint", sdk.NewUint(1)}, - {"rat", sdk.NewRat(1)}, + {"rat", sdk.NewDec(1)}, } assert.NotPanics(t, func() { s.SetString(ctx, kvs[0].key, "test") }) @@ -107,7 +107,7 @@ func TestGetter(t *testing.T) { assert.NotPanics(t, func() { s.SetUint64(ctx, kvs[7].key, uint64(1)) }) assert.NotPanics(t, func() { s.SetInt(ctx, kvs[8].key, sdk.NewInt(1)) }) assert.NotPanics(t, func() { s.SetUint(ctx, kvs[9].key, sdk.NewUint(1)) }) - assert.NotPanics(t, func() { s.SetRat(ctx, kvs[10].key, sdk.NewRat(1)) }) + assert.NotPanics(t, func() { s.SetDec(ctx, kvs[10].key, sdk.NewDec(1)) }) var res interface{} var err error @@ -263,18 +263,18 @@ func TestGetter(t *testing.T) { assert.Equal(t, def9, res) // Rat - def10 := sdk.NewRat(0) - res, err = g.GetRat(ctx, kvs[10].key) + def10 := sdk.NewDec(0) + res, err = g.GetDec(ctx, kvs[10].key) assert.Nil(t, err) assert.Equal(t, kvs[10].param, res) - _, err = g.GetRat(ctx, "invalid") + _, err = g.GetDec(ctx, "invalid") assert.NotNil(t, err) - res = g.GetRatWithDefault(ctx, kvs[10].key, def10) + res = g.GetDecWithDefault(ctx, kvs[10].key, def10) assert.Equal(t, kvs[10].param, res) - res = g.GetRatWithDefault(ctx, "invalid", def10) + res = g.GetDecWithDefault(ctx, "invalid", def10) assert.Equal(t, def10, res) } diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 523a2e220..1e6a4e89d 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -58,7 +58,7 @@ func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { mapp.InitChainer(ctx, req) stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := stake.InitGenesis(ctx, keeper, stakeGenesis) if err != nil { panic(err) @@ -109,7 +109,7 @@ func TestSlashingMsgs(t *testing.T) { validator := checkValidator(t, mapp, stakeKeeper, addr1, true) require.Equal(t, addr1, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} // no signing info yet diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go new file mode 100644 index 000000000..6e2809bfc --- /dev/null +++ b/x/slashing/genesis.go @@ -0,0 +1,14 @@ +package slashing + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" +) + +// InitGenesis initializes the keeper's address to pubkey map. +func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) { + for _, validator := range data.Validators { + keeper.addPubkey(ctx, validator.GetPubKey()) + } + return +} diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 19a48b448..0cb64ab40 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -39,7 +39,7 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { } // Cannot be unrevoked until out of jail - if ctx.BlockHeader().Time < info.JailedUntil { + if ctx.BlockHeader().Time.Before(info.JailedUntil) { return ErrValidatorJailed(k.codespace).Result() } diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 89c1e11a6..41cb5d199 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -20,7 +20,7 @@ func TestCannotUnrevokeUnlessRevoked(t *testing.T) { require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // assert non-revoked validator can't be unrevoked got = slh(ctx, NewMsgUnrevoke(addr)) diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index cd4e69be1..2f163e57a 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -2,10 +2,14 @@ package slashing import ( "fmt" + "time" + + tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/params" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" ) @@ -15,7 +19,6 @@ type Keeper struct { cdc *wire.Codec validatorSet sdk.ValidatorSet params params.Getter - // codespace codespace sdk.CodespaceType } @@ -33,10 +36,10 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params pa } // handle a validator signing two blocks at the same height -func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, timestamp int64, power int64) { +func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, timestamp time.Time, power int64) { logger := ctx.Logger().With("module", "x/slashing") time := ctx.BlockHeader().Time - age := time - timestamp + age := time.Sub(timestamp) address := sdk.ValAddress(pubkey.Address()) // Double sign too old @@ -60,23 +63,26 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti if !found { panic(fmt.Sprintf("Expected signing info for validator %s but not found", address)) } - signInfo.JailedUntil = time + k.DoubleSignUnbondDuration(ctx) + signInfo.JailedUntil = time.Add(k.DoubleSignUnbondDuration(ctx)) k.setValidatorSigningInfo(ctx, address, signInfo) } // handle a validator signature, must be called once per validator per block // nolint gocyclo -func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, power int64, signed bool) { +func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { logger := ctx.Logger().With("module", "x/slashing") height := ctx.BlockHeight() - address := sdk.ValAddress(pubkey.Address()) - + address := sdk.ValAddress(addr) + pubkey, err := k.getPubkey(ctx, addr) + if err != nil { + panic(fmt.Sprintf("Validator address %v not found", addr)) + } // Local index, so counts blocks validator *should* have signed // Will use the 0-value default signing info if not present, except for start height signInfo, found := k.getValidatorSigningInfo(ctx, address) if !found { // If this validator has never been seen before, construct a new SigningInfo with the correct start height - signInfo = NewValidatorSigningInfo(height, 0, 0, 0) + signInfo = NewValidatorSigningInfo(height, 0, time.Unix(0, 0), 0) } index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx) signInfo.IndexOffset++ @@ -98,7 +104,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, } if !signed { - logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx))) + logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", addr, height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx))) } minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) { @@ -109,7 +115,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx))) k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx)) k.validatorSet.Revoke(ctx, pubkey) - signInfo.JailedUntil = ctx.BlockHeader().Time + k.DowntimeUnbondDuration(ctx) + signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx)) } else { // Validator was (a) not found or (b) already revoked, don't slash logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already revoked", @@ -120,3 +126,46 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, // Set the updated signing info k.setValidatorSigningInfo(ctx, address, signInfo) } + +// AddValidators adds the validators to the keepers validator addr to pubkey mapping. +func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.Validator) { + for i := 0; i < len(vals); i++ { + val := vals[i] + pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey) + if err != nil { + panic(err) + } + k.addPubkey(ctx, pubkey) + } +} + +// TODO: Make a method to remove the pubkey from the map when a validator is unbonded. +func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { + addr := pubkey.Address() + k.setAddrPubkeyRelation(ctx, addr, pubkey) +} + +func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) { + store := ctx.KVStore(k.storeKey) + var pubkey crypto.PubKey + err := k.cdc.UnmarshalBinary(store.Get(getAddrPubkeyRelationKey(address)), &pubkey) + if err != nil { + return nil, fmt.Errorf("address %v not found", address) + } + return pubkey, nil +} + +func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinary(pubkey) + store.Set(getAddrPubkeyRelationKey(addr), bz) +} + +func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) { + store := ctx.KVStore(k.storeKey) + store.Delete(getAddrPubkeyRelationKey(addr)) +} + +func getAddrPubkeyRelationKey(address []byte) []byte { + return append([]byte{0x03}, address...) +} diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index fc60c5029..68de7a43c 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -2,13 +2,12 @@ package slashing import ( "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" ) // Have to change these parameters for tests @@ -29,27 +28,28 @@ func TestHandleDoubleSign(t *testing.T) { addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) + keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // handle a signature to set signing info - keeper.handleValidatorSignature(ctx, val, amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) // double sign less than max age - keeper.handleDoubleSign(ctx, val, 0, 0, amtInt) + keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt) // should be revoked require.True(t, sk.Validator(ctx, addr).GetRevoked()) // unrevoke to measure power sk.Unrevoke(ctx, val) // power should be reduced - require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) - ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + keeper.MaxEvidenceAge(ctx)}) + require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))}) // double sign past max age - keeper.handleDoubleSign(ctx, val, 0, 0, amtInt) - require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) + keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt) + require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) } // Test a validator through uptime, downtime, revocation, @@ -64,21 +64,24 @@ func TestHandleAbsentValidator(t *testing.T) { slh := NewHandler(keeper) got := sh(ctx, newTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) + keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.False(t, found) require.Equal(t, int64(0), info.StartHeight) require.Equal(t, int64(0), info.IndexOffset) require.Equal(t, int64(0), info.SignedBlocksCounter) - require.Equal(t, int64(0), info.JailedUntil) + // default time.Time value + var blankTime time.Time + require.Equal(t, blankTime, info.JailedUntil) height := int64(0) // 1000 first blocks OK for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) @@ -88,7 +91,7 @@ func TestHandleAbsentValidator(t *testing.T) { // 500 blocks missed for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) @@ -103,7 +106,7 @@ func TestHandleAbsentValidator(t *testing.T) { // 501st block missed ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) @@ -118,7 +121,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.False(t, got.IsOK()) // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(abci.Header{Time: keeper.DowntimeUnbondDuration(ctx) + 1}) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeUnbondDuration(ctx))}) got = slh(ctx, NewMsgUnrevoke(addr)) require.True(t, got.IsOK()) @@ -128,7 +131,8 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should have been slashed pool = sk.GetPool(ctx) - require.Equal(t, int64(amtInt-1), pool.BondedTokens.RoundInt64()) + slashAmt := sdk.NewDec(amtInt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() + require.Equal(t, int64(amtInt)-slashAmt, pool.BondedTokens.RoundInt64()) // validator start height should have been changed info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) @@ -139,7 +143,7 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should not be immediately revoked again height++ ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) validator, _ = sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Bonded, validator.GetStatus()) @@ -147,14 +151,14 @@ func TestHandleAbsentValidator(t *testing.T) { nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 for ; height < nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } // validator should be revoked again after 500 unsigned blocks nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } validator, _ = sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Unbonded, validator.GetStatus()) @@ -170,24 +174,25 @@ func TestHandleNewValidator(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) require.True(t, got.IsOK()) - stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) + keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}}) - require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower()) + require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower()) // 1000 first blocks not a validator ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) // Now a validator, for two blocks - keeper.handleValidatorSignature(ctx, val, 100, true) + keeper.handleValidatorSignature(ctx, val.Address(), 100, true) ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) - keeper.handleValidatorSignature(ctx, val, 100, false) + keeper.handleValidatorSignature(ctx, val.Address(), 100, false) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.True(t, found) require.Equal(t, int64(keeper.SignedBlocksWindow(ctx)+1), info.StartHeight) require.Equal(t, int64(2), info.IndexOffset) require.Equal(t, int64(1), info.SignedBlocksCounter) - require.Equal(t, int64(0), info.JailedUntil) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) // validator should be bonded still, should not have been revoked or slashed validator, _ := sk.GetValidatorByPubKey(ctx, val) @@ -207,19 +212,20 @@ func TestHandleAlreadyRevoked(t *testing.T) { sh := stake.NewHandler(sk) got := sh(ctx, newTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) - stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) + keeper.AddValidators(ctx, validatorUpdates) // 1000 first blocks OK height := int64(0) for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) } // 501 blocks missed for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } // validator should have been revoked and slashed @@ -231,7 +237,7 @@ func TestHandleAlreadyRevoked(t *testing.T) { // another block missed ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val, amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) // validator should not have been slashed twice validator, _ = sk.GetValidatorByPubKey(ctx, val) diff --git a/x/slashing/params.go b/x/slashing/params.go index 45d2833ce..a25d121c9 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -1,6 +1,8 @@ package slashing import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -17,8 +19,8 @@ const ( // MaxEvidenceAge - Max age for evidence - 21 days (3 weeks) // MaxEvidenceAge = 60 * 60 * 24 * 7 * 3 -func (k Keeper) MaxEvidenceAge(ctx sdk.Context) int64 { - return k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge) +func (k Keeper) MaxEvidenceAge(ctx sdk.Context) time.Duration { + return time.Duration(k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge)) * time.Second } // SignedBlocksWindow - sliding window for downtime slashing @@ -28,29 +30,29 @@ func (k Keeper) SignedBlocksWindow(ctx sdk.Context) int64 { // Downtime slashing thershold - default 50% func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { - minSignedPerWindow := k.params.GetRatWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) + minSignedPerWindow := k.params.GetDecWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) signedBlocksWindow := k.SignedBlocksWindow(ctx) - return sdk.NewRat(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() + return sdk.NewDec(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() } // Double-sign unbond duration -func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) int64 { - return k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration) +func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) time.Duration { + return time.Duration(k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration)) * time.Second } // Downtime unbond duration -func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) int64 { - return k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration) +func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) time.Duration { + return time.Duration(k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration)) * time.Second } // SlashFractionDoubleSign - currently default 5% -func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Rat { - return k.params.GetRatWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) +func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Dec { + return k.params.GetDecWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) } // SlashFractionDowntime - currently default 1% -func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Rat { - return k.params.GetRatWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) +func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Dec { + return k.params.GetDecWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) } // declared as var because of keeper_test.go @@ -70,9 +72,9 @@ var ( // TODO Temporarily set to 10 minutes for testnets defaultDowntimeUnbondDuration int64 = 60 * 10 - defaultMinSignedPerWindow = sdk.NewRat(1, 2) + defaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) - defaultSlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20)) + defaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) - defaultSlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100)) + defaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) ) diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 3118793aa..a21917e79 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -3,6 +3,7 @@ package slashing import ( "encoding/binary" "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -48,7 +49,7 @@ func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ValAddr } // Construct a new `ValidatorSigningInfo` struct -func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil int64, signedBlocksCounter int64) ValidatorSigningInfo { +func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, signedBlocksCounter int64) ValidatorSigningInfo { return ValidatorSigningInfo{ StartHeight: startHeight, IndexOffset: indexOffset, @@ -59,15 +60,15 @@ func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil i // Signing info for a validator type ValidatorSigningInfo struct { - StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked - IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array - JailedUntil int64 `json:"jailed_until"` // timestamp validator cannot be unrevoked until - SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time) + StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked + IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array + JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unrevoked until + SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time) } // Return human readable signing info func (i ValidatorSigningInfo) HumanReadableString() string { - return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %d, signed blocks counter: %d", + return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, signed blocks counter: %d", i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter) } diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index b2da974e7..f92c43581 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -2,6 +2,7 @@ package slashing import ( "testing" + "time" "github.com/stretchr/testify/require" @@ -15,7 +16,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { newInfo := ValidatorSigningInfo{ StartHeight: int64(4), IndexOffset: int64(3), - JailedUntil: int64(2), + JailedUntil: time.Unix(2, 0), SignedBlocksCounter: int64(10), } keeper.setValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]), newInfo) @@ -23,7 +24,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { require.True(t, found) require.Equal(t, info.StartHeight, int64(4)) require.Equal(t, info.IndexOffset, int64(3)) - require.Equal(t, info.JailedUntil, int64(2)) + require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC()) require.Equal(t, info.SignedBlocksCounter, int64(10)) } diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 7b3ab0436..50c501d7e 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -69,7 +69,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace) genesis := stake.DefaultGenesisState() - genesis.Pool.LooseTokens = sdk.NewRat(initCoins.MulRaw(int64(len(addrs))).Int64()) + genesis.Pool.LooseTokens = sdk.NewDec(initCoins.MulRaw(int64(len(addrs))).Int64()) _, err = stake.InitGenesis(ctx, sk, genesis) require.Nil(t, err) diff --git a/x/slashing/tick.go b/x/slashing/tick.go index da157ca7a..7478b511c 100644 --- a/x/slashing/tick.go +++ b/x/slashing/tick.go @@ -20,13 +20,9 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags // Iterate over all the validators which *should* have signed this block // Store whether or not they have actually signed it and slash/unbond any // which have missed too many blocks in a row (downtime slashing) - for _, signingValidator := range req.Validators { + for _, signingValidator := range req.LastCommitInfo.GetValidators() { present := signingValidator.SignedLastBlock - pubkey, err := tmtypes.PB2TM.PubKey(signingValidator.Validator.PubKey) - if err != nil { - panic(err) - } - sk.handleValidatorSignature(ctx, pubkey, signingValidator.Validator.Power, present) + sk.handleValidatorSignature(ctx, signingValidator.Validator.Address, signingValidator.Validator.Power, present) } // Iterate through any newly discovered evidence of infraction diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 38e339e59..b230f9c94 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -2,11 +2,11 @@ package slashing import ( "testing" + "time" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" @@ -19,21 +19,24 @@ func TestBeginBlocker(t *testing.T) { // bond the validator got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, pk, amt)) require.True(t, got.IsOK()) - stake.EndBlocker(ctx, sk) + validatorUpdates := stake.EndBlocker(ctx, sk) + keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) val := abci.Validator{ - PubKey: tmtypes.TM2PB.PubKey(pk), - Power: amt.Int64(), + Address: pk.Address(), + Power: amt.Int64(), } // mark the validator as having signed req := abci.RequestBeginBlock{ - Validators: []abci.SigningValidator{{ - Validator: val, - SignedLastBlock: true, - }}, + LastCommitInfo: abci.LastCommitInfo{ + Validators: []abci.SigningValidator{{ + Validator: val, + SignedLastBlock: true, + }}, + }, } BeginBlocker(ctx, req, keeper) @@ -41,7 +44,7 @@ func TestBeginBlocker(t *testing.T) { require.True(t, found) require.Equal(t, ctx.BlockHeight(), info.StartHeight) require.Equal(t, int64(1), info.IndexOffset) - require.Equal(t, int64(0), info.JailedUntil) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) require.Equal(t, int64(1), info.SignedBlocksCounter) height := int64(0) @@ -50,10 +53,12 @@ func TestBeginBlocker(t *testing.T) { for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ - Validators: []abci.SigningValidator{{ - Validator: val, - SignedLastBlock: true, - }}, + LastCommitInfo: abci.LastCommitInfo{ + Validators: []abci.SigningValidator{{ + Validator: val, + SignedLastBlock: true, + }}, + }, } BeginBlocker(ctx, req, keeper) } @@ -62,10 +67,12 @@ func TestBeginBlocker(t *testing.T) { for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ - Validators: []abci.SigningValidator{{ - Validator: val, - SignedLastBlock: false, - }}, + LastCommitInfo: abci.LastCommitInfo{ + Validators: []abci.SigningValidator{{ + Validator: val, + SignedLastBlock: false, + }}, + }, } BeginBlocker(ctx, req, keeper) } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 4e1b2c2ac..9847ed290 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -63,7 +63,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer { mapp.InitChainer(ctx, req) stakeGenesis := DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := InitGenesis(ctx, keeper, stakeGenesis) if err != nil { @@ -90,14 +90,14 @@ func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper, func checkDelegation( t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr, - validatorAddr sdk.AccAddress, expFound bool, expShares sdk.Rat, + validatorAddr sdk.AccAddress, expFound bool, expShares sdk.Dec, ) { ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr) if expFound { require.True(t, found) - require.True(sdk.RatEq(t, expShares, delegation.Shares)) + require.True(sdk.DecEq(t, expShares, delegation.Shares)) return } @@ -138,7 +138,7 @@ func TestStakeMsgs(t *testing.T) { validator := checkValidator(t, mApp, keeper, addr1, true) require.Equal(t, addr1, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) // addr1 create validator on behalf of addr2 createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(addr1, addr2, priv2.PubKey(), bondCoin, description) @@ -150,10 +150,10 @@ func TestStakeMsgs(t *testing.T) { validator = checkValidator(t, mApp, keeper, addr2, true) require.Equal(t, addr2, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) // check the bond that should have been created as well - checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10)) + checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewDec(10)) // edit the validator description = NewDescription("bar_moniker", "", "", "") @@ -169,14 +169,14 @@ func TestStakeMsgs(t *testing.T) { mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) - checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewRat(10)) + checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewDec(10)) // begin unbonding - beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10)) + beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewDec(10)) mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, priv2) // delegation should exist anymore - checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Rat{}) + checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Dec{}) // balance should be the same because bonding not yet complete mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index 5ac303f9f..dc2c8e30d 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -139,7 +139,10 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command { } // parse out the delegation - delegation := types.MustUnmarshalDelegation(cdc, key, res) + delegation, err := types.UnmarshalDelegation(cdc, key, res) + if err != nil { + return err + } switch viper.Get(cli.OutputFlag) { case "text": diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 4c86777f3..9f04f2ed2 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -239,27 +239,27 @@ func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command { func getShares( storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercentStr string, delegatorAddr, validatorAddr sdk.AccAddress, -) (sharesAmount sdk.Rat, err error) { +) (sharesAmount sdk.Dec, err error) { switch { case sharesAmountStr != "" && sharesPercentStr != "": return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both") case sharesAmountStr == "" && sharesPercentStr == "": return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both") case sharesAmountStr != "": - sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr, types.MaxBondDenominatorPrecision) + sharesAmount, err = sdk.NewDecFromStr(sharesAmountStr) if err != nil { return sharesAmount, err } - if !sharesAmount.GT(sdk.ZeroRat()) { + if !sharesAmount.GT(sdk.ZeroDec()) { return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)") } case sharesPercentStr != "": - var sharesPercent sdk.Rat - sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr, types.MaxBondDenominatorPrecision) + var sharesPercent sdk.Dec + sharesPercent, err = sdk.NewDecFromStr(sharesPercentStr) if err != nil { return sharesAmount, err } - if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) { + if !sharesPercent.GT(sdk.ZeroDec()) || !sharesPercent.LTE(sdk.OneDec()) { return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)") } @@ -273,11 +273,12 @@ func getShares( if err != nil { return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err) } - - delegation := types.MustUnmarshalDelegation(cdc, key, resQuery) + delegation, err := types.UnmarshalDelegation(cdc, key, resQuery) + if err != nil { + return sdk.ZeroDec(), err + } sharesAmount = sharesPercent.Mul(delegation.Shares) } - return } diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 0eba14aac..4dfaa0f7f 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -20,41 +20,54 @@ const storeName = "stake" func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) { - // GET /stake/delegators/{delegatorAddr} // Get all delegations (delegation, undelegation and redelegation) from a delegator + // Get all delegations (delegation, undelegation and redelegation) from a delegator r.HandleFunc( "/stake/delegators/{delegatorAddr}", delegatorHandlerFn(cliCtx, cdc), ).Methods("GET") - // GET /stake/delegators/{delegatorAddr}/txs?type=<bond/unbond/redelegate> // Get all staking txs (i.e msgs) from a delegator + // Get all staking txs (i.e msgs) from a delegator r.HandleFunc( "/stake/delegators/{delegatorAddr}/txs", delegatorTxsHandlerFn(cliCtx, cdc), ).Methods("GET") - // GET /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} // Query a delegation between a delegator and a validator + // Query all validators that a delegator is bonded to + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/validators", + delegatorValidatorsHandlerFn(cliCtx, cdc), + ).Methods("GET") + + // Query a validator that a delegator is bonded to + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/validators/{validatorAddr}", + delegatorValidatorHandlerFn(cliCtx, cdc), + ).Methods("GET") + + // Query a delegation between a delegator and a validator r.HandleFunc( "/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}", delegationHandlerFn(cliCtx, cdc), ).Methods("GET") - // GET /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} // Query all unbonding_delegations between a delegator and a validator + // Query all unbonding_delegations between a delegator and a validator r.HandleFunc( "/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", unbondingDelegationsHandlerFn(cliCtx, cdc), ).Methods("GET") - // GET /stake/validators/ + // Get all validators r.HandleFunc( "/stake/validators", validatorsHandlerFn(cliCtx, cdc), ).Methods("GET") - // GET /stake/validators/{addr} + // Get a single validator info r.HandleFunc( "/stake/validators/{addr}", validatorHandlerFn(cliCtx, cdc), ).Methods("GET") + } // already resolve the rational shares to not handle this in the client @@ -137,17 +150,16 @@ func delegatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handler if statusCode != http.StatusNoContent { delegationSummary.Redelegations = append(delegationSummary.Redelegations, redelegations) } - - output, err := cdc.MarshalJSON(delegationSummary) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - // success - w.Write(output) // write } + + output, err := cdc.MarshalJSON(delegationSummary) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + w.Write(output) } } @@ -218,14 +230,13 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Hand txs = append(txs, foundTxs...) } - // success output, err = cdc.MarshalJSON(txs) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) // write + w.Write(output) } } @@ -336,7 +347,7 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle DelegatorAddr: delegation.DelegatorAddr, ValidatorAddr: delegation.ValidatorAddr, Height: delegation.Height, - Shares: delegation.Shares.FloatString(), + Shares: delegation.Shares.String(), } output, err := cdc.MarshalJSON(outputDelegation) @@ -402,14 +413,13 @@ func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) ht bondedValidators = append(bondedValidators, validator) } - // success output, err := cdc.MarshalJSON(bondedValidators) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) // write + w.Write(output) } } @@ -441,7 +451,6 @@ func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) htt w.WriteHeader(statusCode) return } - // success output, err = cdc.MarshalJSON(validator) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -498,25 +507,40 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handler valAddress, err := sdk.AccAddressFromBech32(bech32validatorAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + w.Write([]byte(fmt.Sprintf("error: %s", err.Error()))) return } - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + key := stake.GetValidatorKey(valAddress) + + res, err := cliCtx.QueryStore(key, storeName) if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + w.Write([]byte(fmt.Sprintf("couldn't query validator, error: %s", err.Error()))) return } - validator, err := getValidator(valAddress, kvs, cdc) + // the query will return empty if there is no data for this record + if len(res) == 0 { + w.WriteHeader(http.StatusNoContent) + return + } + + validator, err := types.UnmarshalValidator(cdc, valAddress, res) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validator. Error: %s", err.Error()))) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - output, err = cdc.MarshalJSON(validator) + bech32Validator, err := validator.Bech32Validator() + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + output, err = cdc.MarshalJSON(bech32Validator) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index fbefc7f21..d8b9b6011 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/wire" authcliCtx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/gorilla/mux" @@ -160,7 +159,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex return } - shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision) + shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) @@ -234,7 +233,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex return } - shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision) + shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index 86e714662..96588f73b 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -1,7 +1,6 @@ package rest import ( - "bytes" "fmt" "net/http" @@ -12,7 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/pkg/errors" rpcclient "github.com/tendermint/tendermint/rpc/client" ) @@ -26,35 +24,36 @@ func contains(stringSlice []string, txType string) bool { return false } -func getDelegatorValidator(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAccAddr sdk.AccAddress) ( - validator types.BechValidator, httpStatusCode int, errMsg string, err error) { +func getDelegatorValidator(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( + bech32Validator types.BechValidator, httpStatusCode int, errMsg string, err error) { - // check if the delegator is bonded or redelegated to the validator - keyDel := stake.GetDelegationKey(delegatorAddr, validatorAccAddr) - - res, err := cliCtx.QueryStore(keyDel, storeName) + key := stake.GetDelegationKey(delegatorAddr, validatorAddr) + res, err := cliCtx.QueryStore(key, storeName) if err != nil { return types.BechValidator{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err } - if len(res) == 0 { return types.BechValidator{}, http.StatusNoContent, "", nil } - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + key = stake.GetValidatorKey(validatorAddr) + res, err = cliCtx.QueryStore(key, storeName) if err != nil { - return types.BechValidator{}, http.StatusInternalServerError, "Error: ", err + return types.BechValidator{}, http.StatusInternalServerError, "couldn't query validator. Error: ", err } - if len(kvs) == 0 { - // the query will return empty if there are no delegations + if len(res) == 0 { return types.BechValidator{}, http.StatusNoContent, "", nil } - - validator, errVal := getValidatorFromAccAdrr(validatorAccAddr, kvs, cdc) - if errVal != nil { - return types.BechValidator{}, http.StatusInternalServerError, "Couldn't get info from validator. Error: ", errVal + validator, err := types.UnmarshalValidator(cdc, validatorAddr, res) + if err != nil { + return types.BechValidator{}, http.StatusBadRequest, "", err } - return validator, http.StatusOK, "", nil + bech32Validator, err = validator.Bech32Validator() + if err != nil { + return types.BechValidator{}, http.StatusBadRequest, "", err + } + + return bech32Validator, http.StatusOK, "", nil } func getDelegatorDelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( @@ -65,7 +64,6 @@ func getDelegatorDelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegat return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err } - // the query will return empty if there is no data for this record if len(marshalledDelegation) == 0 { return DelegationWithoutRat{}, http.StatusNoContent, "", nil } @@ -79,7 +77,7 @@ func getDelegatorDelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegat DelegatorAddr: delegation.DelegatorAddr, ValidatorAddr: delegation.ValidatorAddr, Height: delegation.Height, - Shares: delegation.Shares.FloatString(), + Shares: delegation.Shares.String(), } return outputDelegation, http.StatusOK, "", nil @@ -93,7 +91,6 @@ func getDelegatorUndelegations(cliCtx context.CLIContext, cdc *wire.Codec, deleg return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't query unbonding-delegation. Error: ", err } - // the query will return empty if there is no data for this record if len(marshalledUnbondingDelegation) == 0 { return types.UnbondingDelegation{}, http.StatusNoContent, "", nil } @@ -108,8 +105,8 @@ func getDelegatorUndelegations(cliCtx context.CLIContext, cdc *wire.Codec, deleg func getDelegatorRedelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( regelegations types.Redelegation, httpStatusCode int, errMsg string, err error) { - keyRedelegateTo := stake.GetREDsByDelToValDstIndexKey(delegatorAddr, validatorAddr) - marshalledRedelegations, err := cliCtx.QueryStore(keyRedelegateTo, storeName) + key := stake.GetREDsByDelToValDstIndexKey(delegatorAddr, validatorAddr) + marshalledRedelegations, err := cliCtx.QueryStore(key, storeName) if err != nil { return types.Redelegation{}, http.StatusInternalServerError, "couldn't query redelegation. Error: ", err } @@ -118,7 +115,7 @@ func getDelegatorRedelegations(cliCtx context.CLIContext, cdc *wire.Codec, deleg return types.Redelegation{}, http.StatusNoContent, "", nil } - redelegations, err := types.UnmarshalRED(cdc, keyRedelegateTo, marshalledRedelegations) + redelegations, err := types.UnmarshalRED(cdc, key, marshalledRedelegations) if err != nil { return types.Redelegation{}, http.StatusInternalServerError, "couldn't unmarshall redelegations. Error: ", err } @@ -160,55 +157,10 @@ func getValidators(validatorKVs []sdk.KVPair, cdc *wire.Codec) ([]types.BechVali return validators, nil } -// gets a validator given a ValAddress -func getValidator(address sdk.AccAddress, validatorKVs []sdk.KVPair, cdc *wire.Codec) (stake.BechValidator, error) { - // parse out the validators - for _, kv := range validatorKVs { - addr := kv.Key[1:] - validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) - if err != nil { - return stake.BechValidator{}, err - } - - ownerAddress := validator.PubKey.Address() - if bytes.Equal(ownerAddress.Bytes(), address.Bytes()) { - bech32Validator, err := validator.Bech32Validator() - if err != nil { - return stake.BechValidator{}, err - } - - return bech32Validator, nil - } - } - return stake.BechValidator{}, errors.Errorf("Couldn't find validator") -} - -// gets a validator given an AccAddress -func getValidatorFromAccAdrr(address sdk.AccAddress, validatorKVs []sdk.KVPair, cdc *wire.Codec) (stake.BechValidator, error) { - // parse out the validators - for _, kv := range validatorKVs { - addr := kv.Key[1:] - validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) - if err != nil { - return stake.BechValidator{}, err - } - - ownerAddress := validator.PubKey.Address() - if bytes.Equal(ownerAddress.Bytes(), address.Bytes()) { - bech32Validator, err := validator.Bech32Validator() - if err != nil { - return stake.BechValidator{}, err - } - - return bech32Validator, nil - } - } - return stake.BechValidator{}, errors.Errorf("Couldn't find validator") -} - // gets all Bech32 validators from a key func getBech32Validators(storeName string, cliCtx context.CLIContext, cdc *wire.Codec) ( validators []types.BechValidator, httpStatusCode int, errMsg string, err error) { + // Get all validators using key kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) if err != nil { diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index 2febd2c6a..9cdbe1982 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -17,7 +17,7 @@ func TestInitGenesis(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewRat(2) + pool.BondedTokens = sdk.NewDec(2) params := keeper.GetParams(ctx) var delegations []Delegation @@ -32,11 +32,11 @@ func TestInitGenesis(t *testing.T) { // initialize the validators validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.OneRat() - validators[0].DelegatorShares = sdk.OneRat() + validators[0].Tokens = sdk.OneDec() + validators[0].DelegatorShares = sdk.OneDec() validators[1].Status = sdk.Bonded - validators[1].Tokens = sdk.OneRat() - validators[1].DelegatorShares = sdk.OneRat() + validators[1].Tokens = sdk.OneDec() + validators[1].DelegatorShares = sdk.OneDec() genesisState = types.NewGenesisState(pool, params, validators, delegations) vals, err := InitGenesis(ctx, keeper, genesisState) @@ -69,7 +69,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { // Assigning 2 to the first 100 vals, 1 to the rest pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewRat(int64(200 + (size - 100))) + pool.BondedTokens = sdk.NewDec(int64(200 + (size - 100))) params := keeper.GetParams(ctx) delegations := []Delegation{} @@ -80,11 +80,11 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { validators[i].Status = sdk.Bonded if i < 100 { - validators[i].Tokens = sdk.NewRat(2) - validators[i].DelegatorShares = sdk.NewRat(2) + validators[i].Tokens = sdk.NewDec(2) + validators[i].DelegatorShares = sdk.NewDec(2) } else { - validators[i].Tokens = sdk.OneRat() - validators[i].DelegatorShares = sdk.OneRat() + validators[i].Tokens = sdk.OneDec() + validators[i].DelegatorShares = sdk.OneDec() } } diff --git a/x/stake/handler.go b/x/stake/handler.go index 9a8cee446..8f7475de9 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -1,6 +1,8 @@ package stake import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/tags" @@ -38,7 +40,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid // Process provision inflation blockTime := ctx.BlockHeader().Time - if blockTime-pool.InflationLastTime >= 3600 { + if blockTime.Sub(pool.InflationLastTime) >= time.Hour { params := k.GetParams(ctx) pool.InflationLastTime = blockTime pool = pool.ProcessProvisions(params) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index f183b279a..a06be0f88 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -2,6 +2,7 @@ package stake import ( "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -80,7 +81,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) // slash and revoke the first validator - keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewDecWithPrec(5, 1)) keeper.Revoke(ctx, keep.PKs[0]) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -109,7 +110,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.Equal(t, power2, power3) // unbond self-delegation - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(1000000)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(1000000)) msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) @@ -137,8 +138,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr1, validator.Owner) assert.Equal(t, pk1, validator.PubKey) - assert.Equal(t, sdk.NewRat(10), validator.BondedTokens()) - assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares) + assert.Equal(t, sdk.NewDec(10), validator.BondedTokens()) + assert.Equal(t, sdk.NewDec(10), validator.DelegatorShares) assert.Equal(t, Description{}, validator.Description) // two validators can't have the same owner address @@ -161,8 +162,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr2, validator.Owner) assert.Equal(t, pk2, validator.PubKey) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) } @@ -181,8 +182,8 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, validatorAddr, validator.Owner) assert.Equal(t, pk, validator.PubKey) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) // one validator cannot be created twice even from different delegator @@ -220,7 +221,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { pool := keeper.GetPool(ctx) exRate := validator.DelegatorShareExRate() - require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate) + require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v", exRate) require.Equal(t, bondAmount, pool.BondedTokens.RoundInt64()) // just send the same msgbond multiple times @@ -239,7 +240,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { require.True(t, found) exRate := validator.DelegatorShareExRate() - require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i) + require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v, i = %v", exRate, i) expBond := int64(i+1) * bondAmount expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation) @@ -294,7 +295,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { // just send the same msgUnbond multiple times // TODO use decimals here - unbondShares := sdk.NewRat(10) + unbondShares := sdk.NewDec(10) msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) numUnbonds := 5 @@ -338,7 +339,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { initBond, } for _, c := range errorCases { - unbondShares := sdk.NewRat(int64(c)) + unbondShares := sdk.NewDec(int64(c)) msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.False(t, got.IsOK(), "expected unbond msg to fail") @@ -347,14 +348,14 @@ func TestIncrementsMsgUnbond(t *testing.T) { leftBonded := initBond - int64(numUnbonds)*unbondShares.RoundInt64() // should be unable to unbond one more than we have - unbondShares = sdk.NewRat(leftBonded + 1) + unbondShares = sdk.NewDec(leftBonded + 1) msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.False(t, got.IsOK(), "got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares.String(), leftBonded) // should be able to unbond just what we have - unbondShares = sdk.NewRat(leftBonded) + unbondShares = sdk.NewDec(leftBonded) msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), @@ -390,7 +391,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { for i, validatorAddr := range validatorAddrs { _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewRat(10)) // remove delegation + msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewDec(10)) // remove delegation msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddrs[i], validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -435,7 +436,7 @@ func TestMultipleMsgDelegate(t *testing.T) { // unbond them all for i, delegatorAddr := range delegatorAddrs { - msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -466,7 +467,7 @@ func TestRevokeValidator(t *testing.T) { validator, _ := keeper.GetValidator(ctx, validatorAddr) // unbond the validators bond portion - msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error") @@ -482,7 +483,7 @@ func TestRevokeValidator(t *testing.T) { require.False(t, got.IsOK(), "expected error, got %v", got) // test that the delegator can still withdraw their bonds - msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper) require.True(t, got.IsOK(), "expected no error") @@ -500,7 +501,7 @@ func TestUnbondingPeriod(t *testing.T) { // set the unbonding time params := keeper.GetParams(ctx) - params.UnbondingTime = 7 + params.UnbondingTime = 7 * time.Second keeper.SetParams(ctx, params) // create the validator @@ -509,26 +510,26 @@ func TestUnbondingPeriod(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // begin unbonding - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(10)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error") // cannot complete unbonding at same time msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) - require.True(t, !got.IsOK(), "expected no error") + require.True(t, !got.IsOK(), "expected an error") // cannot complete unbonding at time 6 seconds later origHeader := ctx.BlockHeader() headerTime6 := origHeader - headerTime6.Time += 6 + headerTime6.Time = headerTime6.Time.Add(time.Second * 6) ctx = ctx.WithBlockHeader(headerTime6) got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) - require.True(t, !got.IsOK(), "expected no error") + require.True(t, !got.IsOK(), "expected an error") // can complete unbonding at time 7 seconds later headerTime7 := origHeader - headerTime7.Time += 7 + headerTime7.Time = headerTime7.Time.Add(time.Second * 7) ctx = ctx.WithBlockHeader(headerTime7) got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) require.True(t, got.IsOK(), "expected no error") @@ -541,7 +542,7 @@ func TestRedelegationPeriod(t *testing.T) { // set the unbonding time params := keeper.GetParams(ctx) - params.UnbondingTime = 7 + params.UnbondingTime = 7 * time.Second keeper.SetParams(ctx, params) // create the validators @@ -564,7 +565,7 @@ func TestRedelegationPeriod(t *testing.T) { bal1 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins() // begin redelegate - msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -580,14 +581,14 @@ func TestRedelegationPeriod(t *testing.T) { // cannot complete redelegation at time 6 seconds later origHeader := ctx.BlockHeader() headerTime6 := origHeader - headerTime6.Time += 6 + headerTime6.Time = headerTime6.Time.Add(time.Second * 6) ctx = ctx.WithBlockHeader(headerTime6) got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper) require.True(t, !got.IsOK(), "expected an error") // can complete redelegation at time 7 seconds later headerTime7 := origHeader - headerTime7.Time += 7 + headerTime7.Time = headerTime7.Time.Add(time.Second * 7) ctx = ctx.WithBlockHeader(headerTime7) got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper) require.True(t, got.IsOK(), "expected no error") @@ -616,12 +617,12 @@ func TestTransitiveRedelegation(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // begin redelegate - msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) // cannot redelegation to next validator while first delegation exists - msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewRat(10)) + msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate) @@ -662,7 +663,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) // unbond the valdator-2 - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewRat(30)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewDec(30)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -784,7 +785,7 @@ func TestCliffValidator(t *testing.T) { require.Equal(t, validatorAddr2.Bytes(), cliffVal) // unbond valdator-2 - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewRat(30)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewDec(30)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -797,7 +798,7 @@ func TestCliffValidator(t *testing.T) { require.Equal(t, validatorAddr3.Bytes(), cliffVal) // unbond valdator-1 - msgBeginUnbonding = NewMsgBeginUnbonding(validatorAddr1, validatorAddr1, sdk.NewRat(50)) + msgBeginUnbonding = NewMsgBeginUnbonding(validatorAddr1, validatorAddr1, sdk.NewDec(50)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -831,22 +832,22 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ctx = ctx.WithBlockHeight(1) // begin unbonding 4 stake - msgBeginUnbonding := NewMsgBeginUnbonding(del, valA, sdk.NewRat(4)) + msgBeginUnbonding := NewMsgBeginUnbonding(del, valA, sdk.NewDec(4)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") // begin redelegate 6 stake - msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewRat(6)) + msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewDec(6)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginRedelegate") // destination delegation should have 6 shares delegation, found := keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(6), delegation.Shares) + require.Equal(t, sdk.NewDec(6), delegation.Shares) // slash the validator by half - keeper.Slash(ctx, keep.PKs[0], 0, 20, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 0, 20, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should have been slashed by half unbonding, found := keeper.GetUnbondingDelegation(ctx, del, valA) @@ -861,16 +862,16 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { // destination delegation should have been slashed by half delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(3), delegation.Shares) + require.Equal(t, sdk.NewDec(3), delegation.Shares) // validator power should have been reduced by half validator, found := keeper.GetValidator(ctx, valA) require.True(t, found) - require.Equal(t, sdk.NewRat(5), validator.GetPower()) + require.Equal(t, sdk.NewDec(5), validator.GetPower()) // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) - keeper.Slash(ctx, keep.PKs[0], 2, 10, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 2, 10, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should be unchanged unbonding, found = keeper.GetUnbondingDelegation(ctx, del, valA) @@ -885,7 +886,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { // destination delegation should be unchanged delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(3), delegation.Shares) + require.Equal(t, sdk.NewDec(3), delegation.Shares) // validator power should have been reduced to zero // ergo validator should have been removed from the store diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 23b58108f..4b4d267aa 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -218,7 +218,7 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { // Perform a delegation, set/update everything necessary within the store. func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt sdk.Coin, - validator types.Validator, subtractAccount bool) (newShares sdk.Rat, err sdk.Error) { + validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) { // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner) @@ -226,7 +226,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt delegation = types.Delegation{ DelegatorAddr: delegatorAddr, ValidatorAddr: validator.Owner, - Shares: sdk.ZeroRat(), + Shares: sdk.ZeroDec(), } } @@ -254,7 +254,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt // unbond the the delegation return func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, - shares sdk.Rat) (amount sdk.Rat, err sdk.Error) { + shares sdk.Dec) (amount sdk.Dec, err sdk.Error) { // check if delegation has any shares in it unbond delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) @@ -312,7 +312,7 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr //______________________________________________________________________________________________________ // complete unbonding an unbonding record -func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Rat) sdk.Error { +func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error { // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) @@ -327,7 +327,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk // create the unbonding delegation params := k.GetParams(ctx) - minTime := ctx.BlockHeader().Time + params.UnbondingTime + minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime) balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} ubd := types.UnbondingDelegation{ @@ -351,7 +351,7 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr // ensure that enough time has passed ctxTime := ctx.BlockHeader().Time - if ubd.MinTime > ctxTime { + if ubd.MinTime.After(ctxTime) { return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime) } @@ -365,7 +365,7 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr // complete unbonding an unbonding record func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, - validatorDstAddr sdk.AccAddress, sharesAmount sdk.Rat) sdk.Error { + validatorDstAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error { // check if this is a transitive redelegation if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) { @@ -389,7 +389,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd } // create the unbonding delegation - minTime := ctx.BlockHeader().Time + params.UnbondingTime + minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime) red := types.Redelegation{ DelegatorAddr: delegatorAddr, @@ -415,7 +415,7 @@ func (k Keeper) CompleteRedelegation(ctx sdk.Context, delegatorAddr, validatorSr // ensure that enough time has passed ctxTime := ctx.BlockHeader().Time - if red.MinTime > ctxTime { + if red.MinTime.After(ctxTime) { return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime) } diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 80e903ea6..4333a7494 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -2,6 +2,7 @@ package keeper import ( "testing" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -31,7 +32,7 @@ func TestDelegation(t *testing.T) { bond1to1 := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[0], - Shares: sdk.NewRat(9), + Shares: sdk.NewDec(9), } // check the empty keeper first @@ -45,18 +46,18 @@ func TestDelegation(t *testing.T) { require.True(t, bond1to1.Equal(resBond)) // modify a records, save, and retrieve - bond1to1.Shares = sdk.NewRat(99) + bond1to1.Shares = sdk.NewDec(99) keeper.SetDelegation(ctx, bond1to1) resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.True(t, bond1to1.Equal(resBond)) // add some more records - bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0} - bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1} - bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2} - bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3} - bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4} + bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewDec(9), 0} + bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewDec(9), 1} + bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewDec(9), 2} + bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewDec(9), 3} + bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewDec(9), 4} keeper.SetDelegation(ctx, bond1to2) keeper.SetDelegation(ctx, bond1to3) keeper.SetDelegation(ctx, bond2to1) @@ -115,7 +116,7 @@ func TestUnbondingDelegation(t *testing.T) { DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[0], CreationHeight: 0, - MinTime: 0, + MinTime: time.Unix(0, 0), Balance: sdk.NewInt64Coin("steak", 5), } @@ -141,7 +142,7 @@ func TestUnbondingDelegation(t *testing.T) { func TestUnbondDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) //create a validator and a delegator to that validator validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) @@ -162,8 +163,8 @@ func TestUnbondDelegation(t *testing.T) { keeper.SetDelegation(ctx, delegation) var err error - var amount sdk.Rat - amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6)) + var amount sdk.Dec + amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) require.NoError(t, err) require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation @@ -188,9 +189,9 @@ func TestGetRedelegationsFromValidator(t *testing.T) { ValidatorSrcAddr: addrVals[0], ValidatorDstAddr: addrVals[1], CreationHeight: 0, - MinTime: 0, - SharesSrc: sdk.NewRat(5), - SharesDst: sdk.NewRat(5), + MinTime: time.Unix(0, 0), + SharesSrc: sdk.NewDec(5), + SharesDst: sdk.NewDec(5), } // set and retrieve a record @@ -218,9 +219,9 @@ func TestRedelegation(t *testing.T) { ValidatorSrcAddr: addrVals[0], ValidatorDstAddr: addrVals[1], CreationHeight: 0, - MinTime: 0, - SharesSrc: sdk.NewRat(5), - SharesDst: sdk.NewRat(5), + MinTime: time.Unix(0, 0), + SharesSrc: sdk.NewDec(5), + SharesDst: sdk.NewDec(5), } // test shouldn't have and redelegations @@ -241,8 +242,8 @@ func TestRedelegation(t *testing.T) { require.True(t, has) // modify a records, save, and retrieve - rd.SharesSrc = sdk.NewRat(21) - rd.SharesDst = sdk.NewRat(21) + rd.SharesSrc = sdk.NewDec(21) + rd.SharesDst = sdk.NewDec(21) keeper.SetRedelegation(ctx, rd) resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) diff --git a/x/stake/keeper/keeper_test.go b/x/stake/keeper/keeper_test.go index 3f763ea25..9307ddddf 100644 --- a/x/stake/keeper/keeper_test.go +++ b/x/stake/keeper/keeper_test.go @@ -33,7 +33,7 @@ func TestPool(t *testing.T) { require.True(t, expPool.Equal(resPool)) //modify a params, save, and retrieve - expPool.BondedTokens = sdk.NewRat(777) + expPool.BondedTokens = sdk.NewDec(777) keeper.SetPool(ctx, expPool) resPool = keeper.GetPool(ctx) require.True(t, expPool.Equal(resPool)) diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 9c3311474..aeec44fae 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -69,7 +69,7 @@ func (k Keeper) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) sdk.Val } // total power from the bond -func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat { +func (k Keeper) TotalPower(ctx sdk.Context) sdk.Dec { pool := k.GetPool(ctx) return pool.BondedTokens } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index fb9297e9c..c1fabe441 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -20,15 +20,15 @@ import ( // CONTRACT: // Infraction committed at the current height or at a past height, // not at a height in the future -func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Rat) { +func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) { logger := ctx.Logger().With("module", "x/stake") - if slashFactor.LT(sdk.ZeroRat()) { + if slashFactor.LT(sdk.ZeroDec()) { panic(fmt.Errorf("attempted to slash with a negative slashFactor: %v", slashFactor)) } // Amount of slashing = slash slashFactor * power at time of infraction - slashAmount := sdk.NewRat(power).Mul(slashFactor) + slashAmount := sdk.NewDec(power).Mul(slashFactor) // ref https://github.com/cosmos/cosmos-sdk/issues/1348 // ref https://github.com/cosmos/cosmos-sdk/issues/1471 @@ -89,7 +89,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in } // Cannot decrease balance below zero - tokensToBurn := sdk.MinRat(remainingSlashAmount, validator.Tokens) + tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens) // Get the current pool pool := k.GetPool(ctx) @@ -150,23 +150,23 @@ func (k Keeper) setRevoked(ctx sdk.Context, pubkey crypto.PubKey, revoked bool) // (the amount actually slashed may be less if there's // insufficient stake remaining) func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, - infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) { + infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Dec) { now := ctx.BlockHeader().Time // If unbonding started before this height, stake didn't contribute to infraction if unbondingDelegation.CreationHeight < infractionHeight { - return sdk.ZeroRat() + return sdk.ZeroDec() } - if unbondingDelegation.MinTime < now { + if unbondingDelegation.MinTime.Before(now) { // Unbonding delegation no longer eligible for slashing, skip it // TODO Settle and delete it automatically? - return sdk.ZeroRat() + return sdk.ZeroDec() } // Calculate slash amount proportional to stake contributing to infraction - slashAmount = sdk.NewRatFromInt(unbondingDelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor) + slashAmount = sdk.NewDecFromInt(unbondingDelegation.InitialBalance.Amount).Mul(slashFactor) // Don't slash more tokens than held // Possible since the unbonding delegation may already @@ -194,23 +194,23 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty // (the amount actually slashed may be less if there's // insufficient stake remaining) func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, redelegation types.Redelegation, - infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) { + infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Dec) { now := ctx.BlockHeader().Time // If redelegation started before this height, stake didn't contribute to infraction if redelegation.CreationHeight < infractionHeight { - return sdk.ZeroRat() + return sdk.ZeroDec() } - if redelegation.MinTime < now { + if redelegation.MinTime.Before(now) { // Redelegation no longer eligible for slashing, skip it // TODO Delete it automatically? - return sdk.ZeroRat() + return sdk.ZeroDec() } // Calculate slash amount proportional to stake contributing to infraction - slashAmount = sdk.NewRatFromInt(redelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor) + slashAmount = sdk.NewDecFromInt(redelegation.InitialBalance.Amount).Mul(slashFactor) // Don't slash more tokens than held // Possible since the redelegation may already diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 9566bd1f5..444858f29 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -2,6 +2,7 @@ package keeper import ( "testing" + "time" "github.com/stretchr/testify/require" @@ -18,7 +19,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { params := keeper.GetParams(ctx) pool := keeper.GetPool(ctx) numVals := 3 - pool.LooseTokens = sdk.NewRat(amt * int64(numVals)) + pool.LooseTokens = sdk.NewDec(amt * int64(numVals)) // add numVals validators for i := 0; i < numVals; i++ { @@ -62,7 +63,7 @@ func TestRevocation(t *testing.T) { // tests slashUnbondingDelegation func TestSlashUnbondingDelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation ubd := types.UnbondingDelegation{ @@ -70,7 +71,7 @@ func TestSlashUnbondingDelegation(t *testing.T) { ValidatorAddr: addrVals[0], CreationHeight: 0, // expiration timestamp (beyond which the unbonding delegation shouldn't be slashed) - MinTime: 0, + MinTime: time.Unix(0, 0), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10), Balance: sdk.NewInt64Coin(params.BondDenom, 10), } @@ -81,14 +82,14 @@ func TestSlashUnbondingDelegation(t *testing.T) { require.Equal(t, int64(0), slashAmount.RoundInt64()) // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)}) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) keeper.SetUnbondingDelegation(ctx, ubd) slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) require.Equal(t, int64(0), slashAmount.RoundInt64()) // test valid slash, before expiration timestamp and to which stake contributed oldPool := keeper.GetPool(ctx) - ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)}) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) keeper.SetUnbondingDelegation(ctx, ubd) slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) require.Equal(t, int64(5), slashAmount.RoundInt64()) @@ -105,7 +106,7 @@ func TestSlashUnbondingDelegation(t *testing.T) { // tests slashRedelegation func TestSlashRedelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rd := types.Redelegation{ @@ -114,9 +115,9 @@ func TestSlashRedelegation(t *testing.T) { ValidatorDstAddr: addrVals[1], CreationHeight: 0, // expiration timestamp (beyond which the redelegation shouldn't be slashed) - MinTime: 0, - SharesSrc: sdk.NewRat(10), - SharesDst: sdk.NewRat(10), + MinTime: time.Unix(0, 0), + SharesSrc: sdk.NewDec(10), + SharesDst: sdk.NewDec(10), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10), Balance: sdk.NewInt64Coin(params.BondDenom, 10), } @@ -126,7 +127,7 @@ func TestSlashRedelegation(t *testing.T) { del := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(10), + Shares: sdk.NewDec(10), } keeper.SetDelegation(ctx, del) @@ -137,7 +138,7 @@ func TestSlashRedelegation(t *testing.T) { require.Equal(t, int64(0), slashAmount.RoundInt64()) // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)}) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) keeper.SetRedelegation(ctx, rd) validator, found = keeper.GetValidator(ctx, addrVals[1]) require.True(t, found) @@ -146,7 +147,7 @@ func TestSlashRedelegation(t *testing.T) { // test valid slash, before expiration timestamp and to which stake contributed oldPool := keeper.GetPool(ctx) - ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)}) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) keeper.SetRedelegation(ctx, rd) validator, found = keeper.GetValidator(ctx, addrVals[1]) require.True(t, found) @@ -171,7 +172,7 @@ func TestSlashRedelegation(t *testing.T) { func TestSlashAtFutureHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) require.Panics(t, func() { keeper.Slash(ctx, pk, 1, 10, fraction) }) } @@ -179,7 +180,7 @@ func TestSlashAtFutureHeight(t *testing.T) { func TestSlashAtCurrentHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) oldPool := keeper.GetPool(ctx) validator, found := keeper.GetValidatorByPubKey(ctx, pk) @@ -192,16 +193,16 @@ func TestSlashAtCurrentHeight(t *testing.T) { newPool := keeper.GetPool(ctx) // power decreased - require.Equal(t, sdk.NewRat(5), validator.GetPower()) + require.Equal(t, sdk.NewDec(5), validator.GetPower()) // pool bonded shares decreased - require.Equal(t, sdk.NewRat(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) + require.Equal(t, sdk.NewDec(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) } // tests Slash at a previous height with an unbonding delegation func TestSlashWithUnbondingDelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation ubd := types.UnbondingDelegation{ @@ -209,7 +210,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { ValidatorAddr: addrVals[0], CreationHeight: 11, // expiration timestamp (beyond which the unbonding delegation shouldn't be slashed) - MinTime: 0, + MinTime: time.Unix(0, 0), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4), Balance: sdk.NewInt64Coin(params.BondDenom, 4), } @@ -238,7 +239,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.Equal(t, sdk.NewRat(7), validator.GetPower()) + require.Equal(t, sdk.NewDec(7), validator.GetPower()) // slash validator again ctx = ctx.WithBlockHeight(13) @@ -255,7 +256,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 3 again - require.Equal(t, sdk.NewRat(4), validator.GetPower()) + require.Equal(t, sdk.NewDec(4), validator.GetPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -275,7 +276,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 3 again - require.Equal(t, sdk.NewRat(1), validator.GetPower()) + require.Equal(t, sdk.NewDec(1), validator.GetPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -302,7 +303,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { func TestSlashWithRedelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rd := types.Redelegation{ @@ -310,9 +311,9 @@ func TestSlashWithRedelegation(t *testing.T) { ValidatorSrcAddr: addrVals[0], ValidatorDstAddr: addrVals[1], CreationHeight: 11, - MinTime: 0, - SharesSrc: sdk.NewRat(6), - SharesDst: sdk.NewRat(6), + MinTime: time.Unix(0, 0), + SharesSrc: sdk.NewDec(6), + SharesDst: sdk.NewDec(6), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6), Balance: sdk.NewInt64Coin(params.BondDenom, 6), } @@ -322,13 +323,13 @@ func TestSlashWithRedelegation(t *testing.T) { del := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(6), + Shares: sdk.NewDec(6), } keeper.SetDelegation(ctx, del) // update bonded tokens pool := keeper.GetPool(ctx) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewRat(6)) + pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(6)) keeper.SetPool(ctx, pool) // slash validator @@ -354,13 +355,13 @@ func TestSlashWithRedelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.Equal(t, sdk.NewRat(8), validator.GetPower()) + require.Equal(t, sdk.NewDec(8), validator.GetPower()) // slash the validator again ctx = ctx.WithBlockHeight(12) validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) }) + require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) }) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -375,13 +376,13 @@ func TestSlashWithRedelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 4 - require.Equal(t, sdk.NewRat(4), validator.GetPower()) + require.Equal(t, sdk.NewDec(4), validator.GetPower()) // slash the validator again, by 100% ctx = ctx.WithBlockHeight(12) validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) + keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -403,7 +404,7 @@ func TestSlashWithRedelegation(t *testing.T) { // validator no longer in the store _, found = keeper.GetValidatorByPubKey(ctx, pk) require.False(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) + keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -423,7 +424,7 @@ func TestSlashWithRedelegation(t *testing.T) { // tests Slash at a previous height with both an unbonding delegation and a redelegation func TestSlashBoth(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rdA := types.Redelegation{ @@ -432,9 +433,9 @@ func TestSlashBoth(t *testing.T) { ValidatorDstAddr: addrVals[1], CreationHeight: 11, // expiration timestamp (beyond which the redelegation shouldn't be slashed) - MinTime: 0, - SharesSrc: sdk.NewRat(6), - SharesDst: sdk.NewRat(6), + MinTime: time.Unix(0, 0), + SharesSrc: sdk.NewDec(6), + SharesDst: sdk.NewDec(6), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6), Balance: sdk.NewInt64Coin(params.BondDenom, 6), } @@ -444,7 +445,7 @@ func TestSlashBoth(t *testing.T) { delA := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(6), + Shares: sdk.NewDec(6), } keeper.SetDelegation(ctx, delA) @@ -454,7 +455,7 @@ func TestSlashBoth(t *testing.T) { ValidatorAddr: addrVals[0], CreationHeight: 11, // expiration timestamp (beyond which the unbonding delegation shouldn't be slashed) - MinTime: 0, + MinTime: time.Unix(0, 0), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4), Balance: sdk.NewInt64Coin(params.BondDenom, 4), } @@ -482,5 +483,5 @@ func TestSlashBoth(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, PKs[0]) require.True(t, found) // power not decreased, all stake was bonded since - require.Equal(t, sdk.NewRat(10), validator.GetPower()) + require.Equal(t, sdk.NewDec(10), validator.GetPower()) } diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 010963034..0470c2898 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -77,10 +77,10 @@ func MakeTestCodec() *wire.Codec { // default params without inflation func ParamsNoInflation() types.Params { return types.Params{ - InflationRateChange: sdk.ZeroRat(), - InflationMax: sdk.ZeroRat(), - InflationMin: sdk.ZeroRat(), - GoalBonded: sdk.NewRat(67, 100), + InflationRateChange: sdk.ZeroDec(), + InflationMax: sdk.ZeroDec(), + InflationMin: sdk.ZeroDec(), + GoalBonded: sdk.NewDecWithPrec(67, 2), MaxValidators: 100, BondDenom: "steak", } @@ -119,7 +119,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context {keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)}, }) require.Nil(t, err) - pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(initCoins)) + pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(initCoins)) keeper.SetPool(ctx, pool) } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 8f9399b2a..ce5a19061 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -134,6 +134,8 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat } // get the group of bonded validators sorted by power-rank +// +// TODO: Rename to GetBondedValidatorsByPower or GetValidatorsByPower(ctx, status) func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) maxValidators := k.GetParams(ctx).MaxValidators @@ -191,13 +193,13 @@ func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) { //___________________________________________________________________________ -// Perfom all the necessary steps for when a validator changes its power. This +// Perform all the necessary steps for when a validator changes its power. This // function updates all validator stores as well as tendermint update store. // It may kick out validators if a new validator is entering the bonded validator // group. // // nolint: gocyclo -// TODO: Remove above nolint, function needs to be simplified +// TODO: Remove above nolint, function needs to be simplified! func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator { store := ctx.KVStore(k.storeKey) pool := k.GetPool(ctx) @@ -210,19 +212,29 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type cliffPower := k.GetCliffValidatorPower(ctx) switch { - // if already bonded and power increasing only need to update tendermint + // if the validator is already bonded and the power is increasing, we need + // perform the following: + // a) update Tendermint + // b) check if the cliff validator needs to be updated case powerIncreasing && !validator.Revoked && (oldFound && oldValidator.Status == sdk.Bonded): bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) store.Set(GetTendermintUpdatesKey(validator.Owner), bz) + if cliffPower != nil { + cliffAddr := sdk.AccAddress(k.GetCliffValidator(ctx)) + if bytes.Equal(cliffAddr, validator.Owner) { + k.updateCliffValidator(ctx, validator) + } + } + // if is a new validator and the new power is less than the cliff validator case cliffPower != nil && !oldFound && bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower // skip to completion - // if was unbonded and the new power is less than the cliff validator + // if was unbonded and the new power is less than the cliff validator case cliffPower != nil && (oldFound && oldValidator.Status == sdk.Unbonded) && bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower @@ -232,11 +244,10 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type // a) not-bonded and now has power-rank greater than cliff validator // b) bonded and now has decreased in power default: - // update the validator set for this validator - // if updated, the validator has changed bonding status updatedVal, updated := k.UpdateBondedValidators(ctx, validator) - if updated { // updates to validator occurred to be updated + if updated { + // the validator has changed bonding status validator = updatedVal break } @@ -246,13 +257,69 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) store.Set(GetTendermintUpdatesKey(validator.Owner), bz) } - } k.SetValidator(ctx, validator) return validator } +// updateCliffValidator determines if the current cliff validator needs to be +// updated or swapped. If the provided affected validator is the current cliff +// validator before it's power was increased, either the cliff power key will +// be updated or if it's power is greater than the next bonded validator by +// power, it'll be swapped. +func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validator) { + var newCliffVal types.Validator + + store := ctx.KVStore(k.storeKey) + pool := k.GetPool(ctx) + cliffAddr := sdk.AccAddress(k.GetCliffValidator(ctx)) + + oldCliffVal, found := k.GetValidator(ctx, cliffAddr) + if !found { + panic(fmt.Sprintf("cliff validator record not found for address: %v\n", cliffAddr)) + } + + // NOTE: We get the power via affectedVal since the store (by power key) + // has yet to be updated. + affectedValPower := affectedVal.GetPower() + + // Create a validator iterator ranging from smallest to largest by power + // starting the current cliff validator's power. + start := GetValidatorsByPowerIndexKey(oldCliffVal, pool) + end := sdk.PrefixEndBytes(ValidatorsByPowerIndexKey) + iterator := store.Iterator(start, end) + + if iterator.Valid() { + ownerAddr := iterator.Value() + currVal, found := k.GetValidator(ctx, ownerAddr) + if !found { + panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) + } + + if currVal.Status != sdk.Bonded || currVal.Revoked { + panic(fmt.Sprintf("unexpected revoked or unbonded validator for address: %s\n", ownerAddr)) + } + + newCliffVal = currVal + iterator.Close() + } else { + panic("failed to create valid validator power iterator") + } + + if bytes.Equal(affectedVal.Owner, newCliffVal.Owner) { + // The affected validator remains the cliff validator, however, since + // the store does not contain the new power, set the new cliff + // validator to the affected validator. + bz := GetValidatorsByPowerIndexKey(affectedVal, pool) + store.Set(ValidatorPowerCliffKey, bz) + } else if affectedValPower.GT(newCliffVal.GetPower()) { + // The affected validator no longer remains the cliff validator as it's + // power is greater than the new current cliff validator. + k.setCliffValidator(ctx, newCliffVal, pool) + } +} + func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { if newValidator.Revoked && oldFound && oldValidator.Status == sdk.Bonded { newValidator = k.unbondValidator(ctx, newValidator) @@ -317,8 +384,9 @@ func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidato // // nolint: gocyclo // TODO: Remove the above golint -func (k Keeper) UpdateBondedValidators(ctx sdk.Context, - affectedValidator types.Validator) (updatedVal types.Validator, updated bool) { +func (k Keeper) UpdateBondedValidators( + ctx sdk.Context, affectedValidator types.Validator) ( + updatedVal types.Validator, updated bool) { store := ctx.KVStore(k.storeKey) @@ -328,7 +396,8 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context, var validator, validatorToBond types.Validator newValidatorBonded := false - iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest + // create a validator iterator ranging from largest to smallest by power + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) for { if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { break @@ -348,21 +417,27 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context, } } - // increment bondedValidatorsCount / get the validator to bond - if !validator.Revoked { - if validator.Status != sdk.Bonded { - validatorToBond = validator - newValidatorBonded = true + if validator.Revoked { + // we should no longer consider jailed validators as they are ranked + // lower than any non-jailed/bonded validators + if validator.Status == sdk.Bonded { + panic(fmt.Sprintf("revoked validator cannot be bonded for address: %s\n", ownerAddr)) } - bondedValidatorsCount++ - // sanity check - } else if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr)) + break } + // increment the total number of bonded validators and potentially mark + // the validator to bond + if validator.Status != sdk.Bonded { + validatorToBond = validator + newValidatorBonded = true + } + + bondedValidatorsCount++ iterator.Next() } + iterator.Close() // clear or set the cliff validator @@ -372,17 +447,17 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context, k.clearCliffValidator(ctx) } - // swap the cliff validator for a new validator if the affected validator was bonded + // swap the cliff validator for a new validator if the affected validator + // was bonded if newValidatorBonded { - // unbond the cliff validator if oldCliffValidatorAddr != nil { cliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr)) } - k.unbondValidator(ctx, cliffVal) + k.unbondValidator(ctx, cliffVal) } // bond the new validator @@ -391,12 +466,12 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context, return validator, true } } + return types.Validator{}, false } // full update of the bonded validator set, many can be added/kicked func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) // clear the current validators store, add to the ToKickOut temp store @@ -404,27 +479,26 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) for ; iterator.Valid(); iterator.Next() { ownerAddr := GetAddressFromValBondedIndexKey(iterator.Key()) - toKickOut[string(ownerAddr)] = 0 // set anything + toKickOut[string(ownerAddr)] = 0 } + iterator.Close() + var validator types.Validator + oldCliffValidatorAddr := k.GetCliffValidator(ctx) maxValidators := k.GetParams(ctx).MaxValidators bondedValidatorsCount := 0 - iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest - var validator types.Validator + iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) for { if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { break } - // either retrieve the original validator from the store, - // or under the situation that this is the "new validator" just - // use the validator provided because it has not yet been updated - // in the main validator store - ownerAddr := iterator.Value() var found bool + + ownerAddr := iterator.Value() validator, found = k.GetValidator(ctx, ownerAddr) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) @@ -434,23 +508,26 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { if found { delete(toKickOut, string(ownerAddr)) } else { - - // if it wasn't in the toKickOut group it means - // this wasn't a previously a validator, therefor - // update the validator to enter the validator group + // If the validator wasn't in the toKickOut group it means it wasn't + // previously a validator, therefor update the validator to enter + // the validator group. validator = k.bondValidator(ctx, validator) } - if !validator.Revoked { - bondedValidatorsCount++ - } else { + if validator.Revoked { + // we should no longer consider jailed validators as they are ranked + // lower than any non-jailed/bonded validators if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr)) + panic(fmt.Sprintf("revoked validator cannot be bonded for address: %s\n", ownerAddr)) } + + break } + bondedValidatorsCount++ iterator.Next() } + iterator.Close() // clear or set the cliff validator @@ -460,7 +537,6 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { k.clearCliffValidator(ctx) } - // perform the actual kicks kickOutValidators(k, ctx, toKickOut) return } diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 06273d6af..7b15a9c7f 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -1,6 +1,7 @@ package keeper import ( + "fmt" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,8 +20,8 @@ func TestSetValidator(t *testing.T) { validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, 10) require.Equal(t, sdk.Unbonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) keeper.SetPool(ctx, pool) keeper.UpdateValidator(ctx, validator) @@ -28,8 +29,8 @@ func TestSetValidator(t *testing.T) { validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) // Check each store for being saved resVal, found := keeper.GetValidator(ctx, addrVals[0]) @@ -55,8 +56,8 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { pool := keeper.GetPool(ctx) // create a random pool - pool.LooseTokens = sdk.NewRat(10000) - pool.BondedTokens = sdk.NewRat(1234) + pool.LooseTokens = sdk.NewDec(10000) + pool.BondedTokens = sdk.NewDec(1234) keeper.SetPool(ctx, pool) // add a validator @@ -75,7 +76,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) // burn half the delegator shares - validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2))) + validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2))) require.Equal(t, int64(50), burned.RoundInt64()) keeper.SetPool(ctx, pool) // update the pool keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out @@ -88,6 +89,62 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) } +func TestCliffValidatorChange(t *testing.T) { + numVals := 10 + maxVals := 5 + + // create context, keeper, and pool for tests + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + + // create keeper parameters + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(maxVals) + keeper.SetParams(ctx, params) + + // create a random pool + pool.LooseTokens = sdk.NewDec(10000) + pool.BondedTokens = sdk.NewDec(1234) + keeper.SetPool(ctx, pool) + + validators := make([]types.Validator, numVals) + for i := 0; i < len(validators); i++ { + moniker := fmt.Sprintf("val#%d", int64(i)) + val := types.NewValidator(Addrs[i], PKs[i], types.Description{Moniker: moniker}) + val.BondHeight = int64(i) + val.BondIntraTxCounter = int16(i) + val, pool, _ = val.AddTokensFromDel(pool, int64((i+1)*10)) + + keeper.SetPool(ctx, pool) + val = keeper.UpdateValidator(ctx, val) + validators[i] = val + } + + // add a large amount of tokens to current cliff validator + currCliffVal := validators[numVals-maxVals] + currCliffVal, pool, _ = currCliffVal.AddTokensFromDel(pool, 200) + keeper.SetPool(ctx, pool) + currCliffVal = keeper.UpdateValidator(ctx, currCliffVal) + + // assert new cliff validator to be set to the second lowest bonded validator by power + newCliffVal := validators[numVals-maxVals+1] + require.Equal(t, newCliffVal.Owner, sdk.AccAddress(keeper.GetCliffValidator(ctx))) + + // assert cliff validator power should have been updated + cliffPower := keeper.GetCliffValidatorPower(ctx) + require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower) + + // add small amount of tokens to new current cliff validator + newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, 1) + keeper.SetPool(ctx, pool) + newCliffVal = keeper.UpdateValidator(ctx, newCliffVal) + + // assert cliff validator has not change but increased in power + cliffPower = keeper.GetCliffValidatorPower(ctx) + require.Equal(t, newCliffVal.Owner, sdk.AccAddress(keeper.GetCliffValidator(ctx))) + require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower) +} + func TestSlashToZeroPowerRemoved(t *testing.T) { // initialize setup ctx, _, keeper := CreateTestInput(t, false, 100) @@ -104,7 +161,7 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool) // slash the validator by 100% - keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneRat()) + keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneDec()) // validator should have been deleted _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) @@ -121,13 +178,13 @@ func TestValidatorBasics(t *testing.T) { for i, amt := range amts { validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) validators[i].Status = sdk.Unbonded - validators[i].Tokens = sdk.ZeroRat() + validators[i].Tokens = sdk.ZeroDec() validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt) keeper.SetPool(ctx, pool) } - assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(8), validators[1].Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(7), validators[2].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(9), validators[0].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(8), validators[1].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(7), validators[2].Tokens)) // check the empty keeper first _, found := keeper.GetValidator(ctx, addrVals[0]) @@ -136,7 +193,7 @@ func TestValidatorBasics(t *testing.T) { assert.Zero(t, len(resVals)) pool = keeper.GetPool(ctx) - assert.True(sdk.RatEq(t, sdk.ZeroRat(), pool.BondedTokens)) + assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens)) // set and retrieve a record validators[0] = keeper.UpdateValidator(ctx, validators[0]) @@ -148,15 +205,15 @@ func TestValidatorBasics(t *testing.T) { require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) assert.Equal(t, sdk.Bonded, validators[0].Status) - assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].BondedTokens())) + assert.True(sdk.DecEq(t, sdk.NewDec(9), validators[0].BondedTokens())) pool = keeper.GetPool(ctx) - assert.True(sdk.RatEq(t, pool.BondedTokens, validators[0].BondedTokens())) + assert.True(sdk.DecEq(t, pool.BondedTokens, validators[0].BondedTokens())) // modify a records, save, and retrieve validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(10) - validators[0].DelegatorShares = sdk.NewRat(10) + validators[0].Tokens = sdk.NewDec(10) + validators[0].DelegatorShares = sdk.NewDec(10) validators[0] = keeper.UpdateValidator(ctx, validators[0]) resVal, found = keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) @@ -199,19 +256,19 @@ func GetValidatorSortingUnmixed(t *testing.T) { for i, amt := range amts { validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{}) validators[i].Status = sdk.Bonded - validators[i].Tokens = sdk.NewRat(amt) - validators[i].DelegatorShares = sdk.NewRat(amt) + validators[i].Tokens = sdk.NewDec(amt) + validators[i].DelegatorShares = sdk.NewDec(amt) keeper.UpdateValidator(ctx, validators[i]) } // first make sure everything made it in to the gotValidator group resValidators := keeper.GetValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) - assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(0), resValidators[4].BondedTokens(), "%v", resValidators) assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators) assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators) assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators) @@ -219,14 +276,14 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators) // test a basic increase in voting power - validators[3].Tokens = sdk.NewRat(500) + validators[3].Tokens = sdk.NewDec(500) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) // test a decrease in voting power - validators[3].Tokens = sdk.NewRat(300) + validators[3].Tokens = sdk.NewDec(300) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) @@ -234,7 +291,7 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.True(ValEq(t, validators[4], resValidators[1])) // test equal voting power, different age - validators[3].Tokens = sdk.NewRat(200) + validators[3].Tokens = sdk.NewDec(200) ctx = ctx.WithBlockHeight(10) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) @@ -253,8 +310,8 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.True(ValEq(t, validators[4], resValidators[1])) // change in voting power of both validators, both still in v-set, no age change - validators[3].Tokens = sdk.NewRat(300) - validators[4].Tokens = sdk.NewRat(300) + validators[3].Tokens = sdk.NewDec(300) + validators[4].Tokens = sdk.NewDec(300) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) @@ -281,20 +338,20 @@ func GetValidatorSortingMixed(t *testing.T) { var validators [5]types.Validator for i, amt := range amts { validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{}) - validators[i].DelegatorShares = sdk.NewRat(amt) + validators[i].DelegatorShares = sdk.NewDec(amt) } validators[0].Status = sdk.Bonded validators[1].Status = sdk.Bonded validators[2].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(amts[0]) - validators[1].Tokens = sdk.NewRat(amts[1]) - validators[2].Tokens = sdk.NewRat(amts[2]) + validators[0].Tokens = sdk.NewDec(amts[0]) + validators[1].Tokens = sdk.NewDec(amts[1]) + validators[2].Tokens = sdk.NewDec(amts[2]) validators[3].Status = sdk.Bonded validators[4].Status = sdk.Bonded - validators[3].Tokens = sdk.NewRat(amts[3]) - validators[4].Tokens = sdk.NewRat(amts[4]) + validators[3].Tokens = sdk.NewDec(amts[3]) + validators[4].Tokens = sdk.NewDec(amts[4]) for i := range amts { keeper.UpdateValidator(ctx, validators[i]) @@ -318,11 +375,11 @@ func GetValidatorSortingMixed(t *testing.T) { // first make sure everything made it in to the gotValidator group resValidators := keeper.GetValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) - assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(0), resValidators[4].BondedTokens(), "%v", resValidators) assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators) assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators) assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators) @@ -387,7 +444,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { assert.True(ValEq(t, validators[3], resValidators[1])) // validator 3 kicked out temporarily - validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewRat(201)) + validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201)) keeper.SetPool(ctx, pool) validators[3] = keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) @@ -599,7 +656,7 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { // test single value change // tendermintUpdate set: {} -> {c1'} validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(600) + validators[0].Tokens = sdk.NewDec(600) validators[0] = keeper.UpdateValidator(ctx, validators[0]) updates := keeper.GetTendermintUpdates(ctx) @@ -737,21 +794,21 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // check initial power - require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[0].GetPower().RoundInt64()) - require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[1].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[1].GetPower().RoundInt64()) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} pool := keeper.GetPool(ctx) - validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewRat(20)) - validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewRat(30)) + validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20)) + validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30)) keeper.SetPool(ctx, pool) validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) // power has changed - require.Equal(t, sdk.NewRat(80).RoundInt64(), validators[0].GetPower().RoundInt64()) - require.Equal(t, sdk.NewRat(70).RoundInt64(), validators[1].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64()) // Tendermint updates should reflect power change updates := keeper.GetTendermintUpdates(ctx) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index e4869693c..654d5560b 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -31,7 +31,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim pool := k.GetPool(ctx) loose := sdk.ZeroInt() - bonded := sdk.ZeroRat() + bonded := sdk.ZeroDec() am.IterateAccounts(ctx, func(acc auth.Account) bool { loose = loose.Add(acc.GetCoins().AmountOf("steak")) return false @@ -67,7 +67,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { return func(t *testing.T, app *baseapp.BaseApp, log string) { ctx := app.NewContext(false, abci.Header{}) k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { - require.True(t, validator.GetPower().GT(sdk.ZeroRat()), "validator with non-positive power stored") + require.True(t, validator.GetPower().GT(sdk.ZeroDec()), "validator with non-positive power stored") return false }) } diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index e4077a749..f402d765f 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -132,7 +132,7 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation. msg := stake.MsgBeginUnbonding{ DelegatorAddr: delegatorAddress, ValidatorAddr: validatorAddress, - SharesAmount: sdk.NewRatFromInt(amount), + SharesAmount: sdk.NewDecFromInt(amount), } require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() @@ -191,7 +191,7 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation DelegatorAddr: delegatorAddress, ValidatorSrcAddr: sourceValidatorAddress, ValidatorDstAddr: destValidatorAddress, - SharesAmount: sdk.NewRatFromInt(amount), + SharesAmount: sdk.NewDecFromInt(amount), } require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() @@ -247,7 +247,7 @@ func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup { return false }) pool := k.GetPool(ctx) - pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(loose.Int64(), 1)) + pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(loose.Int64())) k.SetPool(ctx, pool) } } diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 2ceedd5f3..e3227c3ed 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -2,8 +2,8 @@ package types import ( "bytes" - "errors" "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -15,12 +15,12 @@ import ( type Delegation struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.AccAddress `json:"validator_addr"` - Shares sdk.Rat `json:"shares"` + Shares sdk.Dec `json:"shares"` Height int64 `json:"height"` // Last height bond updated } type delegationValue struct { - Shares sdk.Rat + Shares sdk.Dec Height int64 } @@ -47,12 +47,13 @@ func UnmarshalDelegation(cdc *wire.Codec, key, value []byte) (delegation Delegat var storeValue delegationValue err = cdc.UnmarshalBinary(value, &storeValue) if err != nil { + err = fmt.Errorf("%v: %v", ErrNoDelegation(DefaultCodespace).Data(), err) return } addrs := key[1:] // remove prefix bytes if len(addrs) != 2*sdk.AddrLen { - err = errors.New("unexpected key length") + err = fmt.Errorf("%v", ErrBadDelegationAddr(DefaultCodespace).Data()) return } delAddr := sdk.AccAddress(addrs[:sdk.AddrLen]) @@ -80,7 +81,7 @@ var _ sdk.Delegation = Delegation{} // nolint - for sdk.Delegation func (d Delegation) GetDelegator() sdk.AccAddress { return d.DelegatorAddr } func (d Delegation) GetValidator() sdk.AccAddress { return d.ValidatorAddr } -func (d Delegation) GetBondShares() sdk.Rat { return d.Shares } +func (d Delegation) GetBondShares() sdk.Dec { return d.Shares } // HumanReadableString returns a human readable string representation of a // Delegation. An error is returned if the Delegation's delegator or validator @@ -100,14 +101,14 @@ type UnbondingDelegation struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator ValidatorAddr sdk.AccAddress `json:"validator_addr"` // validator unbonding from owner addr CreationHeight int64 `json:"creation_height"` // height which the unbonding took place - MinTime int64 `json:"min_time"` // unix time for unbonding completion + MinTime time.Time `json:"min_time"` // unix time for unbonding completion InitialBalance sdk.Coin `json:"initial_balance"` // atoms initially scheduled to receive at completion Balance sdk.Coin `json:"balance"` // atoms to receive at completion } type ubdValue struct { CreationHeight int64 - MinTime int64 + MinTime time.Time InitialBalance sdk.Coin Balance sdk.Coin } @@ -142,7 +143,7 @@ func UnmarshalUBD(cdc *wire.Codec, key, value []byte) (ubd UnbondingDelegation, addrs := key[1:] // remove prefix bytes if len(addrs) != 2*sdk.AddrLen { - err = errors.New("unexpected key length") + err = fmt.Errorf("%v", ErrBadDelegationAddr(DefaultCodespace).Data()) return } delAddr := sdk.AccAddress(addrs[:sdk.AddrLen]) @@ -186,20 +187,20 @@ type Redelegation struct { ValidatorSrcAddr sdk.AccAddress `json:"validator_src_addr"` // validator redelegation source owner addr ValidatorDstAddr sdk.AccAddress `json:"validator_dst_addr"` // validator redelegation destination owner addr CreationHeight int64 `json:"creation_height"` // height which the redelegation took place - MinTime int64 `json:"min_time"` // unix time for redelegation completion + MinTime time.Time `json:"min_time"` // unix time for redelegation completion InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started Balance sdk.Coin `json:"balance"` // current balance - SharesSrc sdk.Rat `json:"shares_src"` // amount of source shares redelegating - SharesDst sdk.Rat `json:"shares_dst"` // amount of destination shares redelegating + SharesSrc sdk.Dec `json:"shares_src"` // amount of source shares redelegating + SharesDst sdk.Dec `json:"shares_dst"` // amount of destination shares redelegating } type redValue struct { CreationHeight int64 - MinTime int64 + MinTime time.Time InitialBalance sdk.Coin Balance sdk.Coin - SharesSrc sdk.Rat - SharesDst sdk.Rat + SharesSrc sdk.Dec + SharesDst sdk.Dec } // return the redelegation without fields contained within the key for the store @@ -234,7 +235,7 @@ func UnmarshalRED(cdc *wire.Codec, key, value []byte) (red Redelegation, err err addrs := key[1:] // remove prefix bytes if len(addrs) != 3*sdk.AddrLen { - err = errors.New("unexpected key length") + err = fmt.Errorf("%v", ErrBadRedelegationAddr(DefaultCodespace).Data()) return } delAddr := sdk.AccAddress(addrs[:sdk.AddrLen]) diff --git a/x/stake/types/delegation_test.go b/x/stake/types/delegation_test.go index 640f5d979..5624b7101 100644 --- a/x/stake/types/delegation_test.go +++ b/x/stake/types/delegation_test.go @@ -2,6 +2,7 @@ package types import ( "testing" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -11,19 +12,19 @@ func TestDelegationEqual(t *testing.T) { d1 := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } d2 := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } ok := d1.Equal(d2) require.True(t, ok) d2.ValidatorAddr = addr3 - d2.Shares = sdk.NewRat(200) + d2.Shares = sdk.NewDec(200) ok = d1.Equal(d2) require.False(t, ok) @@ -33,7 +34,7 @@ func TestDelegationHumanReadableString(t *testing.T) { d := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } // NOTE: Being that the validator's keypair is random, we cannot test the @@ -57,8 +58,8 @@ func TestUnbondingDelegationEqual(t *testing.T) { require.True(t, ok) ud2.ValidatorAddr = addr3 - ud2.MinTime = 20 * 20 * 2 + ud2.MinTime = time.Unix(20*20*2, 0) ok = ud1.Equal(ud2) require.False(t, ok) } @@ -91,9 +92,9 @@ func TestRedelegationEqual(t *testing.T) { ok := r1.Equal(r2) require.True(t, ok) - r2.SharesDst = sdk.NewRat(10) - r2.SharesSrc = sdk.NewRat(20) - r2.MinTime = 20 * 20 * 2 + r2.SharesDst = sdk.NewDec(10) + r2.SharesSrc = sdk.NewDec(20) + r2.MinTime = time.Unix(20*20*2, 0) ok = r1.Equal(r2) require.False(t, ok) @@ -104,8 +105,8 @@ func TestRedelegationHumanReadableString(t *testing.T) { DelegatorAddr: addr1, ValidatorSrcAddr: addr2, ValidatorDstAddr: addr3, - SharesDst: sdk.NewRat(10), - SharesSrc: sdk.NewRat(20), + SharesDst: sdk.NewDec(10), + SharesSrc: sdk.NewDec(20), } // NOTE: Being that the validator's keypair is random, we cannot test the diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 2ef747bae..d44ed411c 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -3,6 +3,7 @@ package types import ( "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -16,6 +17,7 @@ const ( CodeInvalidDelegation CodeType = 102 CodeInvalidInput CodeType = 103 CodeValidatorJailed CodeType = 104 + CodeInvalidAddress CodeType = sdk.CodeInvalidAddress CodeUnauthorized CodeType = sdk.CodeUnauthorized CodeInternal CodeType = sdk.CodeInternal CodeUnknownRequest CodeType = sdk.CodeUnknownRequest @@ -26,6 +28,10 @@ func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil") } +func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidAddress, "validator address is invalid") +} + func ErrNoValidatorFound(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") } @@ -67,6 +73,10 @@ func ErrBadDenom(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "invalid coin denomination") } +func ErrBadDelegationAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "unexpected address length for this (address, validator) pair") +} + func ErrBadDelegationAmount(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "amount must be > 0") } @@ -99,18 +109,11 @@ func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0") } -func ErrBadSharesPrecision(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDelegation, - fmt.Sprintf("shares denominator must be < %s, try reducing the number of decimal points", - maximumBondingRationalDenominator.String()), - ) -} - func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1") } -func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min int64) sdk.Error { +func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min time.Time) sdk.Error { msg := fmt.Sprintf("%v is not mature requires a min %v of %v, currently it is %v", operation, descriptor, got, min) return sdk.NewError(codespace, CodeUnauthorized, msg) @@ -124,6 +127,10 @@ func ErrExistingUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "existing unbonding delegation found") } +func ErrBadRedelegationAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "unexpected address length for this (address, srcValidator, dstValidator) tuple") +} + func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found") } diff --git a/x/stake/types/inflation_test.go b/x/stake/types/inflation_test.go index 0114b1e05..fd181af3c 100644 --- a/x/stake/types/inflation_test.go +++ b/x/stake/types/inflation_test.go @@ -23,30 +23,30 @@ func TestGetInflation(t *testing.T) { tests := []struct { name string setBondedTokens, setLooseTokens, - setInflation, expectedChange sdk.Rat + setInflation, expectedChange sdk.Dec }{ // with 0% bonded atom supply the inflation should increase by InflationRateChange - {"test 1", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)}, + {"test 1", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), params.InflationRateChange.Quo(hrsPerYrDec)}, // 100% bonded, starting at 20% inflation and being reduced // (1 - (1/0.67))*(0.13/8667) - {"test 2", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(20, 100), - sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)}, + {"test 2", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), + sdk.OneDec().Sub(sdk.OneDec().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)}, // 50% bonded, starting at 10% inflation and being increased - {"test 3", sdk.OneRat(), sdk.OneRat(), sdk.NewRat(10, 100), - sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)}, + {"test 3", sdk.OneDec(), sdk.OneDec(), sdk.NewDecWithPrec(10, 2), + sdk.OneDec().Sub(sdk.NewDecWithPrec(5, 1).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)}, // test 7% minimum stop (testing with 100% bonded) - {"test 4", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(7, 100), sdk.ZeroRat()}, - {"test 5", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)}, + {"test 4", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), sdk.ZeroDec()}, + {"test 5", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(70001, 6), sdk.NewDecWithPrec(-1, 6)}, // test 20% maximum stop (testing with 0% bonded) - {"test 6", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(20, 100), sdk.ZeroRat()}, - {"test 7", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)}, + {"test 6", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), sdk.ZeroDec()}, + {"test 7", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(199999, 6), sdk.NewDecWithPrec(1, 6)}, // perfect balance shouldn't change inflation - {"test 8", sdk.NewRat(67), sdk.NewRat(33), sdk.NewRat(15, 100), sdk.ZeroRat()}, + {"test 8", sdk.NewDec(67), sdk.NewDec(33), sdk.NewDecWithPrec(15, 2), sdk.ZeroDec()}, } for _, tc := range tests { pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens @@ -67,77 +67,80 @@ func TestProcessProvisions(t *testing.T) { var ( initialTotalTokens int64 = 550000000 - cumulativeExpProvs = sdk.ZeroRat() + cumulativeExpProvs = sdk.ZeroDec() ) - pool.LooseTokens = sdk.NewRat(initialTotalTokens) + pool.LooseTokens = sdk.NewDec(initialTotalTokens) // process the provisions for a year for hr := 0; hr < 100; hr++ { - var expProvisions sdk.Rat + var expProvisions sdk.Dec _, expProvisions, pool = updateProvisions(t, pool, params, hr) cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions) } //get the pool and do the final value checks from checkFinalPoolValues - checkFinalPoolValues(t, pool, sdk.NewRat(initialTotalTokens), cumulativeExpProvs) + checkFinalPoolValues(t, pool, sdk.NewDec(initialTotalTokens), cumulativeExpProvs) } //_________________________________________________________________________________________ ////////////////////////////////HELPER FUNCTIONS BELOW///////////////////////////////////// // Final check on the global pool values for what the total tokens accumulated from each hour of provisions -func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Rat) { +func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Dec) { calculatedTotalTokens := initialTotalTokens.Add(cumulativeExpProvs) - require.True(sdk.RatEq(t, calculatedTotalTokens, pool.TokenSupply())) + require.True(sdk.DecEq(t, calculatedTotalTokens, pool.TokenSupply())) } // Processes provisions are added to the pool correctly every hour // Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests -func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Rat, sdk.Rat, Pool) { +func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Dec, sdk.Dec, Pool) { expInflation := pool.NextInflation(params) - expProvisions := expInflation.Mul(pool.TokenSupply().Round(precision)).Quo(hrsPerYrRat) + expProvisions := expInflation.Mul(pool.TokenSupply()).Quo(hrsPerYrDec) startTotalSupply := pool.TokenSupply() pool = pool.ProcessProvisions(params) //check provisions were added to pool - require.True(sdk.RatEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply())) + require.True(sdk.DecEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply())) return expInflation, expProvisions, pool } // Checks that The inflation will correctly increase or decrease after an update to the pool // nolint: gocyclo -func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) { +func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Dec, msg string) { inflationChange := updatedInflation.Sub(previousInflation) switch { //BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation - case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)): - require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg) + case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.LT(sdk.NewDecWithPrec(20, 2)): + require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg) //BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio - case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)): - if previousInflation.Equal(sdk.NewRat(20, 100)) { + case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.Equal(sdk.NewDecWithPrec(20, 2)): + if previousInflation.Equal(sdk.NewDecWithPrec(20, 2)) { require.Equal(t, true, inflationChange.IsZero(), msg) //This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%) } else { - require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg) + require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg) } //ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7% - case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)): - require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg) + case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) && + updatedInflation.LT(sdk.NewDecWithPrec(20, 2)) && updatedInflation.GT(sdk.NewDecWithPrec(7, 2)): + require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg) //ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%. - case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)): - if previousInflation.Equal(sdk.NewRat(7, 100)) { + case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) && + updatedInflation.Equal(sdk.NewDecWithPrec(7, 2)): + + if previousInflation.Equal(sdk.NewDecWithPrec(7, 2)) { require.Equal(t, true, inflationChange.IsZero(), msg) //This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%) } else { - require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg) + require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg) } } } diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 27edad5dd..282000294 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -1,7 +1,6 @@ package types import ( - "math" "reflect" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,18 +10,11 @@ import ( // name to idetify transaction types const MsgType = "stake" -// Maximum amount of decimal points in the decimal representation of rationals -// used in MsgBeginUnbonding / MsgBeginRedelegate -const MaxBondDenominatorPrecision = 8 - // Verify interface at compile time var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{} var _, _ sdk.Msg = &MsgBeginUnbonding{}, &MsgCompleteUnbonding{} var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{} -// Initialize Int for the denominator -var maximumBondingRationalDenominator = sdk.NewInt(int64(math.Pow10(MaxBondDenominatorPrecision))) - //______________________________________________________________________ // MsgCreateValidator - struct for unbonding transactions @@ -211,11 +203,11 @@ type MsgBeginRedelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorSrcAddr sdk.AccAddress `json:"validator_src_addr"` ValidatorDstAddr sdk.AccAddress `json:"validator_dst_addr"` - SharesAmount sdk.Rat `json:"shares_amount"` + SharesAmount sdk.Dec `json:"shares_amount"` } func NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, - validatorDstAddr sdk.AccAddress, sharesAmount sdk.Rat) MsgBeginRedelegate { + validatorDstAddr sdk.AccAddress, sharesAmount sdk.Dec) MsgBeginRedelegate { return MsgBeginRedelegate{ DelegatorAddr: delegatorAddr, @@ -261,12 +253,9 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error { if msg.ValidatorDstAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if msg.SharesAmount.LTE(sdk.ZeroRat()) { + if msg.SharesAmount.LTE(sdk.ZeroDec()) { return ErrBadSharesAmount(DefaultCodespace) } - if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) { - return ErrBadSharesPrecision(DefaultCodespace) - } return nil } @@ -322,10 +311,10 @@ func (msg MsgCompleteRedelegate) ValidateBasic() sdk.Error { type MsgBeginUnbonding struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.AccAddress `json:"validator_addr"` - SharesAmount sdk.Rat `json:"shares_amount"` + SharesAmount sdk.Dec `json:"shares_amount"` } -func NewMsgBeginUnbonding(delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Rat) MsgBeginUnbonding { +func NewMsgBeginUnbonding(delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Dec) MsgBeginUnbonding { return MsgBeginUnbonding{ DelegatorAddr: delegatorAddr, ValidatorAddr: validatorAddr, @@ -362,12 +351,9 @@ func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if msg.SharesAmount.LTE(sdk.ZeroRat()) { + if msg.SharesAmount.LTE(sdk.ZeroDec()) { return ErrBadSharesAmount(DefaultCodespace) } - if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) { - return ErrBadSharesPrecision(DefaultCodespace) - } return nil } diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index 82f92d9f3..2be34fb20 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -143,15 +143,15 @@ func TestMsgBeginRedelegate(t *testing.T) { delegatorAddr sdk.AccAddress validatorSrcAddr sdk.AccAddress validatorDstAddr sdk.AccAddress - sharesAmount sdk.Rat + sharesAmount sdk.Dec expectPass bool }{ - {"regular", addr1, addr2, addr3, sdk.NewRat(1, 10), true}, - {"negative decimal", addr1, addr2, addr3, sdk.NewRat(-1, 10), false}, - {"zero amount", addr1, addr2, addr3, sdk.ZeroRat(), false}, - {"empty delegator", emptyAddr, addr1, addr3, sdk.NewRat(1, 10), false}, - {"empty source validator", addr1, emptyAddr, addr3, sdk.NewRat(1, 10), false}, - {"empty destination validator", addr1, addr2, emptyAddr, sdk.NewRat(1, 10), false}, + {"regular", addr1, addr2, addr3, sdk.NewDecWithPrec(1, 1), true}, + {"negative decimal", addr1, addr2, addr3, sdk.NewDecWithPrec(-1, 1), false}, + {"zero amount", addr1, addr2, addr3, sdk.ZeroDec(), false}, + {"empty delegator", emptyAddr, addr1, addr3, sdk.NewDecWithPrec(1, 1), false}, + {"empty source validator", addr1, emptyAddr, addr3, sdk.NewDecWithPrec(1, 1), false}, + {"empty destination validator", addr1, addr2, emptyAddr, sdk.NewDecWithPrec(1, 1), false}, } for _, tc := range tests { @@ -195,14 +195,14 @@ func TestMsgBeginUnbonding(t *testing.T) { name string delegatorAddr sdk.AccAddress validatorAddr sdk.AccAddress - sharesAmount sdk.Rat + sharesAmount sdk.Dec expectPass bool }{ - {"regular", addr1, addr2, sdk.NewRat(1, 10), true}, - {"negative decimal", addr1, addr2, sdk.NewRat(-1, 10), false}, - {"zero amount", addr1, addr2, sdk.ZeroRat(), false}, - {"empty delegator", emptyAddr, addr1, sdk.NewRat(1, 10), false}, - {"empty validator", addr1, emptyAddr, sdk.NewRat(1, 10), false}, + {"regular", addr1, addr2, sdk.NewDecWithPrec(1, 1), true}, + {"negative decimal", addr1, addr2, sdk.NewDecWithPrec(-1, 1), false}, + {"zero amount", addr1, addr2, sdk.ZeroDec(), false}, + {"empty delegator", emptyAddr, addr1, sdk.NewDecWithPrec(1, 1), false}, + {"empty validator", addr1, emptyAddr, sdk.NewDecWithPrec(1, 1), false}, } for _, tc := range tests { diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 5a7dd6ef5..f297f3105 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -2,22 +2,23 @@ package types import ( "bytes" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) // defaultUnbondingTime reflects three weeks in seconds as the default // unbonding time. -const defaultUnbondingTime int64 = 60 * 60 * 24 * 3 +const defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second // Params defines the high level settings for staking type Params struct { - InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate - InflationMax sdk.Rat `json:"inflation_max"` // maximum inflation rate - InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate - GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms + InflationRateChange sdk.Dec `json:"inflation_rate_change"` // maximum annual change in inflation rate + InflationMax sdk.Dec `json:"inflation_max"` // maximum inflation rate + InflationMin sdk.Dec `json:"inflation_min"` // minimum inflation rate + GoalBonded sdk.Dec `json:"goal_bonded"` // Goal of percent bonded atoms - UnbondingTime int64 `json:"unbonding_time"` + UnbondingTime time.Duration `json:"unbonding_time"` MaxValidators uint16 `json:"max_validators"` // maximum number of validators BondDenom string `json:"bond_denom"` // bondable coin denomination @@ -33,10 +34,10 @@ func (p Params) Equal(p2 Params) bool { // DefaultParams returns a default set of parameters. func DefaultParams() Params { return Params{ - InflationRateChange: sdk.NewRat(13, 100), - InflationMax: sdk.NewRat(20, 100), - InflationMin: sdk.NewRat(7, 100), - GoalBonded: sdk.NewRat(67, 100), + InflationRateChange: sdk.NewDecWithPrec(13, 2), + InflationMax: sdk.NewDecWithPrec(20, 2), + InflationMin: sdk.NewDecWithPrec(7, 2), + GoalBonded: sdk.NewDecWithPrec(67, 2), UnbondingTime: defaultUnbondingTime, MaxValidators: 100, BondDenom: "steak", diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index 6ddadf94b..d59c1ed25 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -3,21 +3,22 @@ package types import ( "bytes" "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) // Pool - dynamic parameters of the current state type Pool struct { - LooseTokens sdk.Rat `json:"loose_tokens"` // tokens which are not bonded in a validator - BondedTokens sdk.Rat `json:"bonded_tokens"` // reserve of bonded tokens - InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time - Inflation sdk.Rat `json:"inflation"` // current annual inflation rate + LooseTokens sdk.Dec `json:"loose_tokens"` // tokens which are not bonded in a validator + BondedTokens sdk.Dec `json:"bonded_tokens"` // reserve of bonded tokens + InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time + Inflation sdk.Dec `json:"inflation"` // current annual inflation rate DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily) // Fee Related - PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations + PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations } // nolint @@ -30,48 +31,48 @@ func (p Pool) Equal(p2 Pool) bool { // initial pool for testing func InitialPool() Pool { return Pool{ - LooseTokens: sdk.ZeroRat(), - BondedTokens: sdk.ZeroRat(), - InflationLastTime: 0, - Inflation: sdk.NewRat(7, 100), + LooseTokens: sdk.ZeroDec(), + BondedTokens: sdk.ZeroDec(), + InflationLastTime: time.Unix(0, 0), + Inflation: sdk.NewDecWithPrec(7, 2), DateLastCommissionReset: 0, - PrevBondedShares: sdk.ZeroRat(), + PrevBondedShares: sdk.ZeroDec(), } } //____________________________________________________________________ // Sum total of all staking tokens in the pool -func (p Pool) TokenSupply() sdk.Rat { +func (p Pool) TokenSupply() sdk.Dec { return p.LooseTokens.Add(p.BondedTokens) } //____________________________________________________________________ // get the bond ratio of the global state -func (p Pool) BondedRatio() sdk.Rat { +func (p Pool) BondedRatio() sdk.Dec { supply := p.TokenSupply() - if supply.GT(sdk.ZeroRat()) { + if supply.GT(sdk.ZeroDec()) { return p.BondedTokens.Quo(supply) } - return sdk.ZeroRat() + return sdk.ZeroDec() } //_______________________________________________________________________ -func (p Pool) looseTokensToBonded(bondedTokens sdk.Rat) Pool { +func (p Pool) looseTokensToBonded(bondedTokens sdk.Dec) Pool { p.BondedTokens = p.BondedTokens.Add(bondedTokens) p.LooseTokens = p.LooseTokens.Sub(bondedTokens) - if p.LooseTokens.LT(sdk.ZeroRat()) { + if p.LooseTokens.LT(sdk.ZeroDec()) { panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p)) } return p } -func (p Pool) bondedTokensToLoose(bondedTokens sdk.Rat) Pool { +func (p Pool) bondedTokensToLoose(bondedTokens sdk.Dec) Pool { p.BondedTokens = p.BondedTokens.Sub(bondedTokens) p.LooseTokens = p.LooseTokens.Add(bondedTokens) - if p.BondedTokens.LT(sdk.ZeroRat()) { + if p.BondedTokens.LT(sdk.ZeroDec()) { panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p)) } return p @@ -81,14 +82,14 @@ func (p Pool) bondedTokensToLoose(bondedTokens sdk.Rat) Pool { // Inflation const precision = 10000 // increased to this precision for accuracy -var hrsPerYrRat = sdk.NewRat(8766) // as defined by a julian year of 365.25 days +var hrsPerYrDec = sdk.NewDec(8766) // as defined by a julian year of 365.25 days // process provisions for an hour period func (p Pool) ProcessProvisions(params Params) Pool { p.Inflation = p.NextInflation(params) provisions := p.Inflation. - Mul(p.TokenSupply().Round(precision)). - Quo(hrsPerYrRat) + Mul(p.TokenSupply()). + Quo(hrsPerYrDec) // TODO add to the fees provisions p.LooseTokens = p.LooseTokens.Add(provisions) @@ -96,7 +97,7 @@ func (p Pool) ProcessProvisions(params Params) Pool { } // get the next inflation rate for the hour -func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { +func (p Pool) NextInflation(params Params) (inflation sdk.Dec) { // The target annual inflation rate is recalculated for each previsions cycle. The // inflation is also subject to a rate change (positive or negative) depending on @@ -105,11 +106,11 @@ func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { // 7% and 20%. // (1 - bondedRatio/GoalBonded) * InflationRateChange - inflationRateChangePerYear := sdk.OneRat(). - Sub(p.BondedRatio().Round(precision). + inflationRateChangePerYear := sdk.OneDec(). + Sub(p.BondedRatio(). Quo(params.GoalBonded)). Mul(params.InflationRateChange) - inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat) + inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrDec) // increase the new annual inflation for this next cycle inflation = p.Inflation.Add(inflationRateChange) @@ -120,5 +121,5 @@ func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { inflation = params.InflationMin } - return inflation.Round(precision) + return inflation } diff --git a/x/stake/types/pool_test.go b/x/stake/types/pool_test.go index 43a2eac06..4541edd3d 100644 --- a/x/stake/types/pool_test.go +++ b/x/stake/types/pool_test.go @@ -11,28 +11,28 @@ func TestPoolEqual(t *testing.T) { p1 := InitialPool() p2 := InitialPool() require.True(t, p1.Equal(p2)) - p2.BondedTokens = sdk.NewRat(3) + p2.BondedTokens = sdk.NewDec(3) require.False(t, p1.Equal(p2)) } func TestAddBondedTokens(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) - pool.BondedTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) + pool.BondedTokens = sdk.NewDec(10) - pool = pool.looseTokensToBonded(sdk.NewRat(10)) + pool = pool.looseTokensToBonded(sdk.NewDec(10)) - require.True(sdk.RatEq(t, sdk.NewRat(20), pool.BondedTokens)) - require.True(sdk.RatEq(t, sdk.NewRat(0), pool.LooseTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(20), pool.BondedTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(0), pool.LooseTokens)) } func TestRemoveBondedTokens(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) - pool.BondedTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) + pool.BondedTokens = sdk.NewDec(10) - pool = pool.bondedTokensToLoose(sdk.NewRat(5)) + pool = pool.bondedTokensToLoose(sdk.NewDec(5)) - require.True(sdk.RatEq(t, sdk.NewRat(5), pool.BondedTokens)) - require.True(sdk.RatEq(t, sdk.NewRat(15), pool.LooseTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(5), pool.BondedTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(15), pool.LooseTokens)) } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 837b8f8e8..bd7d76993 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "errors" "fmt" abci "github.com/tendermint/tendermint/abci/types" @@ -26,21 +25,21 @@ type Validator struct { Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators + Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators Description Description `json:"description"` // description terms for the validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer - Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators - CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge - CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission - CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) + Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission + CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) // fee related - LastBondedTokens sdk.Rat `json:"prev_bonded_tokens"` // Previous bonded tokens held + LastBondedTokens sdk.Dec `json:"prev_bonded_tokens"` // Previous bonded tokens held } // NewValidator - initialize a new validator @@ -50,17 +49,17 @@ func NewValidator(owner sdk.AccAddress, pubKey crypto.PubKey, description Descri PubKey: pubKey, Revoked: false, Status: sdk.Unbonded, - Tokens: sdk.ZeroRat(), - DelegatorShares: sdk.ZeroRat(), + Tokens: sdk.ZeroDec(), + DelegatorShares: sdk.ZeroDec(), Description: description, BondHeight: int64(0), BondIntraTxCounter: int16(0), ProposerRewardPool: sdk.Coins{}, - Commission: sdk.ZeroRat(), - CommissionMax: sdk.ZeroRat(), - CommissionChangeRate: sdk.ZeroRat(), - CommissionChangeToday: sdk.ZeroRat(), - LastBondedTokens: sdk.ZeroRat(), + Commission: sdk.ZeroDec(), + CommissionMax: sdk.ZeroDec(), + CommissionChangeRate: sdk.ZeroDec(), + CommissionChangeToday: sdk.ZeroDec(), + LastBondedTokens: sdk.ZeroDec(), } } @@ -69,17 +68,17 @@ type validatorValue struct { PubKey crypto.PubKey Revoked bool Status sdk.BondStatus - Tokens sdk.Rat - DelegatorShares sdk.Rat + Tokens sdk.Dec + DelegatorShares sdk.Dec Description Description BondHeight int64 BondIntraTxCounter int16 ProposerRewardPool sdk.Coins - Commission sdk.Rat - CommissionMax sdk.Rat - CommissionChangeRate sdk.Rat - CommissionChangeToday sdk.Rat - LastBondedTokens sdk.Rat + Commission sdk.Dec + CommissionMax sdk.Dec + CommissionChangeRate sdk.Dec + CommissionChangeToday sdk.Dec + LastBondedTokens sdk.Dec } // return the redelegation without fields contained within the key for the store @@ -115,17 +114,16 @@ func MustUnmarshalValidator(cdc *wire.Codec, ownerAddr, value []byte) Validator // unmarshal a redelegation from a store key and value func UnmarshalValidator(cdc *wire.Codec, ownerAddr, value []byte) (validator Validator, err error) { + if len(ownerAddr) != sdk.AddrLen { + err = fmt.Errorf("%v", ErrBadValidatorAddr(DefaultCodespace).Data()) + return + } var storeValue validatorValue err = cdc.UnmarshalBinary(value, &storeValue) if err != nil { return } - if len(ownerAddr) != 20 { - err = errors.New("unexpected address length") - return - } - return Validator{ Owner: ownerAddr, PubKey: storeValue.PubKey, @@ -159,8 +157,8 @@ func (v Validator) HumanReadableString() (string, error) { resp += fmt.Sprintf("Validator: %s\n", bechVal) resp += fmt.Sprintf("Revoked: %v\n", v.Revoked) resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status)) - resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.FloatString()) - resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.FloatString()) + resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.String()) + resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String()) resp += fmt.Sprintf("Description: %s\n", v.Description) resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight) resp += fmt.Sprintf("Proposer Reward Pool: %s\n", v.ProposerRewardPool.String()) @@ -182,21 +180,21 @@ type BechValidator struct { Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators + Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators Description Description `json:"description"` // description terms for the validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer - Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators - CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge - CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission - CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) + Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission + CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) // fee related - LastBondedTokens sdk.Rat `json:"prev_bonded_shares"` // last bonded token amount + LastBondedTokens sdk.Dec `json:"prev_bonded_shares"` // last bonded token amount } // get the bech validator from the the regular validator @@ -364,7 +362,7 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, } // removes tokens from a validator -func (v Validator) RemoveTokens(pool Pool, tokens sdk.Rat) (Validator, Pool) { +func (v Validator) RemoveTokens(pool Pool, tokens sdk.Dec) (Validator, Pool) { if v.Status == sdk.Bonded { pool = pool.bondedTokensToLoose(tokens) } @@ -376,25 +374,25 @@ func (v Validator) RemoveTokens(pool Pool, tokens sdk.Rat) (Validator, Pool) { //_________________________________________________________________________________________________________ // AddTokensFromDel adds tokens to a validator -func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Rat) { +func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Dec) { // bondedShare/delegatedShare exRate := v.DelegatorShareExRate() - amountRat := sdk.NewRat(amount) + amountDec := sdk.NewDec(amount) if v.Status == sdk.Bonded { - pool = pool.looseTokensToBonded(amountRat) + pool = pool.looseTokensToBonded(amountDec) } - v.Tokens = v.Tokens.Add(amountRat) - issuedShares := amountRat.Quo(exRate) + v.Tokens = v.Tokens.Add(amountDec) + issuedShares := amountDec.Quo(exRate) v.DelegatorShares = v.DelegatorShares.Add(issuedShares) return v, pool, issuedShares } // RemoveDelShares removes delegator shares from a validator. -func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Pool, sdk.Rat) { +func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Dec) (Validator, Pool, sdk.Dec) { issuedTokens := v.DelegatorShareExRate().Mul(delShares) v.Tokens = v.Tokens.Sub(issuedTokens) v.DelegatorShares = v.DelegatorShares.Sub(delShares) @@ -408,19 +406,19 @@ func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Poo // DelegatorShareExRate gets the exchange rate of tokens over delegator shares. // UNITS: tokens/delegator-shares -func (v Validator) DelegatorShareExRate() sdk.Rat { +func (v Validator) DelegatorShareExRate() sdk.Dec { if v.DelegatorShares.IsZero() { - return sdk.OneRat() + return sdk.OneDec() } return v.Tokens.Quo(v.DelegatorShares) } // Get the bonded tokens which the validator holds -func (v Validator) BondedTokens() sdk.Rat { +func (v Validator) BondedTokens() sdk.Dec { if v.Status == sdk.Bonded { return v.Tokens } - return sdk.ZeroRat() + return sdk.ZeroDec() } //______________________________________________________________________ @@ -434,7 +432,7 @@ func (v Validator) GetMoniker() string { return v.Description.Moniker } func (v Validator) GetStatus() sdk.BondStatus { return v.Status } func (v Validator) GetOwner() sdk.AccAddress { return v.Owner } func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey } -func (v Validator) GetPower() sdk.Rat { return v.BondedTokens() } -func (v Validator) GetTokens() sdk.Rat { return v.Tokens } -func (v Validator) GetDelegatorShares() sdk.Rat { return v.DelegatorShares } +func (v Validator) GetPower() sdk.Dec { return v.BondedTokens() } +func (v Validator) GetTokens() sdk.Dec { return v.Tokens } +func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } func (v Validator) GetBondHeight() int64 { return v.BondHeight } diff --git a/x/stake/types/validator_test.go b/x/stake/types/validator_test.go index 536f44849..3981f5754 100644 --- a/x/stake/types/validator_test.go +++ b/x/stake/types/validator_test.go @@ -3,6 +3,7 @@ package types import ( "fmt" "testing" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" @@ -74,19 +75,19 @@ func TestRemoveTokens(t *testing.T) { Owner: addr1, PubKey: pk1, Status: sdk.Bonded, - Tokens: sdk.NewRat(100), - DelegatorShares: sdk.NewRat(100), + Tokens: sdk.NewDec(100), + DelegatorShares: sdk.NewDec(100), } pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) pool.BondedTokens = validator.BondedTokens() validator, pool = validator.UpdateStatus(pool, sdk.Bonded) require.Equal(t, sdk.Bonded, validator.Status) // remove tokens and test check everything - validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10)) + validator, pool = validator.RemoveTokens(pool, sdk.NewDec(10)) require.Equal(t, int64(90), validator.Tokens.RoundInt64()) require.Equal(t, int64(90), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(20), pool.LooseTokens.RoundInt64()) @@ -97,7 +98,7 @@ func TestRemoveTokens(t *testing.T) { require.Equal(t, int64(0), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(110), pool.LooseTokens.RoundInt64()) - validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10)) + validator, pool = validator.RemoveTokens(pool, sdk.NewDec(10)) require.Equal(t, int64(80), validator.Tokens.RoundInt64()) require.Equal(t, int64(0), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(110), pool.LooseTokens.RoundInt64()) @@ -105,43 +106,43 @@ func TestRemoveTokens(t *testing.T) { func TestAddTokensValidatorBonded(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Bonded) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) } func TestAddTokensValidatorUnbonding(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Unbonding) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) assert.Equal(t, sdk.Unbonding, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) } func TestAddTokensValidatorUnbonded(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) assert.Equal(t, sdk.Unbonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) } // TODO refactor to make simpler like the AddToken tests above @@ -150,16 +151,16 @@ func TestRemoveDelShares(t *testing.T) { Owner: addr1, PubKey: pk1, Status: sdk.Bonded, - Tokens: sdk.NewRat(100), - DelegatorShares: sdk.NewRat(100), + Tokens: sdk.NewDec(100), + DelegatorShares: sdk.NewDec(100), } poolA := InitialPool() - poolA.LooseTokens = sdk.NewRat(10) + poolA.LooseTokens = sdk.NewDec(10) poolA.BondedTokens = valA.BondedTokens() - require.Equal(t, valA.DelegatorShareExRate(), sdk.OneRat()) + require.Equal(t, valA.DelegatorShareExRate(), sdk.OneDec()) // Remove delegator shares - valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewRat(10)) + valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewDec(10)) assert.Equal(t, int64(10), coinsB.RoundInt64()) assert.Equal(t, int64(90), valB.DelegatorShares.RoundInt64()) assert.Equal(t, int64(90), valB.BondedTokens().RoundInt64()) @@ -167,13 +168,13 @@ func TestRemoveDelShares(t *testing.T) { assert.Equal(t, int64(20), poolB.LooseTokens.RoundInt64()) // conservation of tokens - require.True(sdk.RatEq(t, + require.True(sdk.DecEq(t, poolB.LooseTokens.Add(poolB.BondedTokens), poolA.LooseTokens.Add(poolA.BondedTokens))) // specific case from random tests - poolTokens := sdk.NewRat(5102) - delShares := sdk.NewRat(115) + poolTokens := sdk.NewDec(5102) + delShares := sdk.NewDec(115) validator := Validator{ Owner: addr1, PubKey: pk1, @@ -182,22 +183,27 @@ func TestRemoveDelShares(t *testing.T) { DelegatorShares: delShares, } pool := Pool{ - BondedTokens: sdk.NewRat(248305), - LooseTokens: sdk.NewRat(232147), - InflationLastTime: 0, - Inflation: sdk.NewRat(7, 100), + BondedTokens: sdk.NewDec(248305), + LooseTokens: sdk.NewDec(232147), + InflationLastTime: time.Unix(0, 0), + Inflation: sdk.NewDecWithPrec(7, 2), } - shares := sdk.NewRat(29) + shares := sdk.NewDec(29) _, newPool, tokens := validator.RemoveDelShares(pool, shares) - require.True(sdk.RatEq(t, sdk.NewRat(147958, 115), tokens)) - require.True(sdk.RatEq(t, + + exp, err := sdk.NewDecFromStr("1286.5913043477") + require.NoError(t, err) + + require.True(sdk.DecEq(t, exp, tokens)) + + require.True(sdk.DecEq(t, newPool.LooseTokens.Add(newPool.BondedTokens), pool.LooseTokens.Add(pool.BondedTokens))) } func TestUpdateStatus(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(100) + pool.LooseTokens = sdk.NewDec(100) validator := NewValidator(addr1, pk1, Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, 100) @@ -220,8 +226,8 @@ func TestUpdateStatus(t *testing.T) { } func TestPossibleOverflow(t *testing.T) { - poolTokens := sdk.NewRat(2159) - delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664)) + poolTokens := sdk.NewDec(2159) + delShares := sdk.NewDec(391432570689183511).Quo(sdk.NewDec(40113011844664)) validator := Validator{ Owner: addr1, PubKey: pk1, @@ -230,17 +236,17 @@ func TestPossibleOverflow(t *testing.T) { DelegatorShares: delShares, } pool := Pool{ - LooseTokens: sdk.NewRat(100), + LooseTokens: sdk.NewDec(100), BondedTokens: poolTokens, - InflationLastTime: 0, - Inflation: sdk.NewRat(7, 100), + InflationLastTime: time.Unix(0, 0), + Inflation: sdk.NewDecWithPrec(7, 2), } tokens := int64(71) msg := fmt.Sprintf("validator %#v", validator) newValidator, _, _ := validator.AddTokensFromDel(pool, tokens) msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg) - require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroRat()), + require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroDec()), "Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v", msg, newValidator.DelegatorShareExRate()) }