diff --git a/.circleci/config.yml b/.circleci/config.yml index bfa00f377..f8dbab6ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,25 +1,42 @@ -version: 2.1 +version: 2 defaults: &linux_defaults - working_directory: /go/src/core + working_directory: /go/src/github.com/terra-project/core docker: - - image: circleci/golang:1.12.3 + - image: circleci/golang:1.12.7 + environment: + GO111MODULE: 'on' + +############ +# +# Configure macos integration tests + +macos_config: &macos_defaults + macos: + xcode: "10.1.0" + working_directory: $HOME/project/src/github.com/terra-project/core environment: - GOBIN: /tmp/workspace/bin + GO_VERSION: "1.12.5" -deps: &dependencies +set_macos_env: &macos_env run: - name: dependencies + name: Set environment command: | - export PATH="$GOBIN:$PATH" + echo 'export PATH=$PATH:$HOME/go/bin' >> $BASH_ENV + echo 'export GOPATH=$HOME/project' >> $BASH_ENV + echo 'export PATH=$PATH:$HOME/go/bin:$GOPATH/bin' >> $BASH_ENV + echo 'export GO111MODULE=on' + +############ +# +# Configure docs deployment -refs: - filter_only_branches: &filter_only_branches - filters: - branches: - only: /^(develop|master).*/ - tags: - only: /v[0-9]+(\.[0-9]+)*(-.*)*/ +# docs_update: &docs_deploy +# working_directory: ~/repo +# docker: +# - image: tendermintdev/jq_curl +# environment: +# AWS_REGION: us-east-1 jobs: setup_dependencies: @@ -31,53 +48,140 @@ jobs: - restore_cache: keys: - go-mod-v1-{{ checksum "go.sum" }} - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - *dependencies - run: name: binaries command: | - export PATH="$GOBIN:$PATH" + export PATH=/tmp/workspace/bin:$PATH make go-mod-cache make install - save_cache: key: go-mod-v1-{{ checksum "go.sum" }} paths: - "/go/pkg/mod" + - run: + name: tools + command: | + make tools TOOLS_DESTDIR=/tmp/workspace/bin + cp $GOPATH/bin/runsim /tmp/workspace/bin/ - persist_to_workspace: root: /tmp/workspace paths: - bin - profiles - lint: + integration_tests: <<: *linux_defaults parallelism: 1 steps: - attach_workspace: at: /tmp/workspace - checkout - - *dependencies - restore_cache: keys: - go-mod-v1-{{ checksum "go.sum" }} - run: - name: Lint source + name: Test cli command: | - export PATH="$GOBIN:$PATH" - make ci-lint + export BUILDDIR=`pwd`/build + make check-build - test_cover: + test_sim_terra_nondeterminism: + <<: *linux_defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test individual module simulations + command: | + make sim-terra-nondeterminism + + test_sim_terra_fast: <<: *linux_defaults parallelism: 1 steps: - attach_workspace: at: /tmp/workspace - checkout - - *dependencies + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test full Terra simulation + command: | + make sim-terra-fast + + test_sim_terra_import_export: + <<: *linux_defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test Terra import/export simulation + command: | + /tmp/workspace/bin/runsim -j 4 github.com/terra-project/core/app 50 5 TestTerraImportExport + + test_sim_terra_simulation_after_import: + <<: *linux_defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test Terra import/export simulation + command: | + /tmp/workspace/bin/runsim -j 4 github.com/terra-project/core/app 50 5 TestAppSimulationAfterImport + + test_sim_terra_multi_seed_long: + <<: *linux_defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test multi-seed Terra simulation long + command: | + /tmp/workspace/bin/runsim -j 4 github.com/terra-project/core/app 500 50 TestFullAppSimulation + + test_sim_terra_multi_seed: + <<: *linux_defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - restore_cache: + keys: + - go-mod-v1-{{ checksum "go.sum" }} + - run: + name: Test multi-seed Terra simulation short + command: | + /tmp/workspace/bin/runsim -j 4 github.com/terra-project/core/app 50 10 TestFullAppSimulation + + test_cover: + <<: *linux_defaults + parallelism: 4 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout - run: mkdir -p /tmp/logs - restore_cache: keys: @@ -85,10 +189,9 @@ jobs: - run: name: Run tests command: | - export PATH="$GOBIN:$PATH" export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')" export GO111MODULE=on - for pkg in $(go list ./... | grep -v github.com/terra-project/core/cmd/terra/cli_test | grep -v '/simulation'); do + for pkg in $(go list ./... | grep -v github.com/terra-project/core/cli_test | grep -v '/simulation' | circleci tests split --split-by=timings); do id=$(echo "$pkg" | sed 's|[/.]|_|g') go test -mod=readonly -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic -tags='ledger test_ledger_mock' "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" done @@ -118,11 +221,11 @@ jobs: - attach_workspace: at: /tmp/workspace - checkout - - *dependencies - run: name: gather command: | set -ex + echo "--> Concatenating profiles:" ls /tmp/workspace/profiles/ echo "mode: atomic" > coverage.txt @@ -141,6 +244,88 @@ jobs: name: upload command: bash <(curl -s https://codecov.io/bash) -f coverage.txt + localnet: + working_directory: /home/circleci/.go_workspace/src/github.com/terra-project/core + machine: + image: circleci/classic:latest + environment: + GOPATH: /home/circleci/.go_workspace/ + GOOS: linux + GOARCH: amd64 + GO_VERSION: "1.12.5" + parallelism: 1 + steps: + - checkout + - run: + name: run localnet and exit on failure + command: | + pushd /tmp + wget https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz + sudo tar -xvf go$GO_VERSION.linux-amd64.tar.gz + sudo rm -rf /usr/local/go + sudo mv go /usr/local + popd + set -x + make tools + make build-linux + make build-docker-terradnode + make localnet-start + ./contrib/localnet-blocks-test.sh 40 5 10 localhost + + # deploy_docs: + # <<: *docs_deploy + # steps: + # - checkout + # - run: + # name: Trigger website build + # command: | + # curl --silent \ + # --show-error \ + # -X POST \ + # --header "Content-Type: application/json" \ + # -d "{\"branch\": \"$CIRCLE_BRANCH\"}" \ + # "https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$WEBSITE_REPO_NAME/build?circle-token=$TENDERBOT_API_TOKEN" > response.json + + # RESULT=`jq -r '.status' response.json` + # MESSAGE=`jq -r '.message' response.json` + + # if [[ ${RESULT} == "null" ]] || [[ ${RESULT} -ne "200" ]]; then + # echo "CircleCI API call failed: $MESSAGE" + # exit 1 + # else + # echo "Website build started" + # fi + + macos_ci: + <<: *macos_defaults + steps: + - *macos_env + - run: + name: Install go + command: | + source $BASH_ENV + curl -L -O https://dl.google.com/go/go$GO_VERSION.darwin-amd64.tar.gz + tar -C $HOME -xzf go$GO_VERSION.darwin-amd64.tar.gz + rm go$GO_VERSION.darwin-amd64.tar.gz + go version + - checkout + - run: + name: Install SDK + command: | + source $BASH_ENV + make tools + make install + - run: + name: Integration tests + command: + source $BASH_ENV + make check-build + - run: + name: Test full terra simulation + command: | + source $BASH_ENV + make sim-terra-fast + docker_image: <<: *linux_defaults steps: @@ -150,126 +335,154 @@ jobs: - setup_remote_docker: docker_layer_caching: true - run: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then - TERRA_VERSION="stable" - elif [ "${CIRCLE_BRANCH}" == "develop" ]; then - TERRA_VERSION="develop" + TERRAD_VERSION='' + if [ "${CIRCLE_BRANCH}" = "master" ]; then + TERRAD_VERSION="stable" + elif [ "${CIRCLE_BRANCH}" = "develop" ]; then + TERRAD_VERSION="develop" + fi + if [ -z "${TERRAD_VERSION}" ]; then + docker build . else - TERRA_VERSION=`/tmp/workspace/bin/terrad version` + docker build -t tendermint/terra:$TERRAD_VERSION . + # docker login --password-stdin -u $DOCKER_USER <<<$DOCKER_PASS + # docker push tendermint/terra:$TERRAD_VERSION fi - docker build -t ${AWS_RESOURCE_NAME_PREFIX}:${TERRA_VERSION} . - mkdir -p docker-image - docker save -o docker-image/image.tar ${AWS_RESOURCE_NAME_PREFIX}:${TERRA_VERSION} - - persist_to_workspace: - root: . - paths: docker-image - push_to_ecr: - docker: - - image: wilson208/circleci-awscli + docker_tagged: + <<: *linux_defaults steps: - attach_workspace: at: /tmp/workspace + - checkout - setup_remote_docker: docker_layer_caching: true - run: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then - TERRA_VERSION="stable" - elif [ "${CIRCLE_BRANCH}" == "develop" ]; then - TERRA_VERSION="develop" - else - TERRA_VERSION=`/tmp/workspace/bin/terrad version` - fi - eval $(aws --region ${AWS_DEFAULT_REGION} ecr --no-include-email get-login) - docker load --input /tmp/workspace/docker-image/image.tar - docker tag ${AWS_RESOURCE_NAME_PREFIX}:${TERRA_VERSION} ${AWS_ECR_URL}/${AWS_RESOURCE_NAME_PREFIX}:${TERRA_VERSION} - docker push ${AWS_ECR_URL}/${AWS_RESOURCE_NAME_PREFIX}:${TERRA_VERSION} - - localnet: - working_directory: /home/circleci/.go_workspace/src/core - machine: - image: circleci/classic:latest - environment: - GOBIN: /home/circleci/.go_workspace/bin - GOPATH: /home/circleci/.go_workspace/ - GOOS: linux - GOARCH: amd64 - GO_VERSION: "1.11.5" - parallelism: 1 + docker build -t tendermint/terra:$CIRCLE_TAG . + # docker login --password-stdin -u $DOCKER_USER <<<$DOCKER_PASS + # docker push tendermint/terra:$CIRCLE_TAG + reproducible_builds: + <<: *linux_defaults steps: + - attach_workspace: + at: /tmp/workspace - checkout + - setup_remote_docker: + docker_layer_caching: true - run: - name: run localnet and exit on failure + name: Build terra + no_output_timeout: 20m command: | - pushd /tmp - wget https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz - sudo tar -xvf go$GO_VERSION.linux-amd64.tar.gz - sudo rm -rf /usr/local/go - sudo mv go /usr/local - popd - - make go-mod-cache - make build-linux - make build-docker-terradnode - make localnet-start - - CNT=0 - ITER=40 - SLEEP=5 - NUMBLOCKS=10 - NODEADDR=localhost - - while [ ${CNT} -lt $ITER ]; do - var=$((curl -s --fail $NODEADDR:26657/status || echo "{\"result\":{\"sync_info\":{\"latest_block_height\":0}}}") | jq -r '.result.sync_info.latest_block_height') - echo "Number of Blocks: ${var}" - if [ ! -z ${var} ] && [ ${var} -gt ${NUMBLOCKS} ]; then - echo "Number of blocks reached, exiting success..." - exit 0 - fi - let CNT=CNT+1 - sleep $SLEEP + sudo apt-get install -y ruby + bash -x ./contrib/gitian-build.sh all + for os in darwin linux windows; do + cp gitian-build-${os}/result/terra-${os}-res.yml . + rm -rf gitian-build-${os}/ done + - store_artifacts: + path: /go/src/github.com/terra-project/core/terra-darwin-res.yml + - store_artifacts: + path: /go/src/github.com/terra-project/core/terra-linux-res.yml + - store_artifacts: + path: /go/src/github.com/terra-project/core/terra-windows-res.yml - echo "Timeout reached, exiting failure..." - exit 1 +# FIXME: The `setup-contract-tests-data` make target is broken as it completely +# overrides the .terrad directory. +# +# contract_tests: +# <<: *linux_defaults +# steps: +# - attach_workspace: +# at: /tmp/workspace +# - checkout +# - setup_remote_docker: +# docker_layer_caching: true +# - run: +# name: Get Node.js and test REST implementation against swagger documentation at https://swagger.terra.money/rpc/ +# command: | +# go get github.com/snikch/goodman/cmd/goodman +# make build +# make build-contract-tests-hooks +# make setup-contract-tests-data +# export PATH=~/.local/bin:$PATH +# ./contrib/get_node.sh && make contract-tests workflows: version: 2 - build_and_push: + test-suite: jobs: - - setup_dependencies: *filter_only_branches - - lint: - <<: *filter_only_branches + - docker_image: requires: - setup_dependencies - - test_cover: - <<: *filter_only_branches + - docker_tagged: + filters: + tags: + only: + - /^v.*/ + branches: + ignore: + - /.*/ requires: - setup_dependencies - - localnet: - <<: *filter_only_branches - - docker_image: - <<: *filter_only_branches + - macos_ci: + filters: + branches: + only: + - master + - develop + # - deploy_docs: + # filters: + # branches: + # only: + # - master + # - develop + - setup_dependencies: + # filters here are needed to enable this job also for tags + filters: + tags: + only: + - /^v.*/ + - integration_tests: requires: - setup_dependencies - - push_to_ecr: - <<: *filter_only_branches + - test_sim_terra_nondeterminism: requires: - - lint - - docker_image - - localnet + - setup_dependencies + - test_sim_terra_fast: + requires: + - setup_dependencies + - test_sim_terra_import_export: + requires: + - setup_dependencies + - test_sim_terra_simulation_after_import: + requires: + - setup_dependencies + - test_sim_terra_multi_seed: + requires: + - setup_dependencies + - test_sim_terra_multi_seed_long: + requires: + - setup_dependencies + filters: + branches: + only: + - master + - develop + - test_cover: + requires: + - setup_dependencies + - localnet - upload_coverage: - <<: *filter_only_branches requires: - test_cover - - release: + - reproducible_builds: filters: branches: - ignore: /.*/ - tags: - only: /v[0-9]+(\.[0-9]+)*(-.*)*/ + only: + - master requires: - - lint - - test_cover - - localnet + - setup_dependencies +# - contract_tests: +# requires: +# - setup_dependencies diff --git a/.gitignore b/.gitignore index c06e0eee3..577385541 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ mytestnet # Testing coverage.txt profile.out +sim_log_file # Vagrant .vagrant/ diff --git a/.golangci.yml b/.golangci.yml index ce8010e7a..b261ffbff 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,6 +6,12 @@ linters: - ineffassign - unconvert - misspell + - govet + # - unused + # - deadcode + - goconst + - gosec + # - staticcheck linters-settings: gocyclo: min-complexity: 11 @@ -13,5 +19,8 @@ linters-settings: ignore: fmt:.*,io/ioutil:^Read.*,github.com/spf13/cobra:MarkFlagRequired,github.com/spf13/viper:BindPFlag golint: min-confidence: 1.1 +issues: + exclude: + - composite run: tests: false diff --git a/.goreleaser.yml b/.goreleaser.yml index a932b5413..443d10054 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,7 +2,7 @@ project_name: terra before: hooks: - - make get_tools + - make tools - make go-mod-cache builds: diff --git a/Dockerfile b/Dockerfile index 51b65ccab..24e5d49b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ COPY . . # Install minimum necessary dependencies, build Cosmos SDK, remove packages RUN apk add --no-cache $PACKAGES && \ - make get_tools && \ + make tools && \ make go-mod-cache && \ make build-linux && \ make install diff --git a/Makefile b/Makefile index cf52740dd..c49caacb9 100755 --- a/Makefile +++ b/Makefile @@ -1,17 +1,15 @@ -PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') +#!/usr/bin/make -f + PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') LEDGER_ENABLED ?= true -GOTOOLS = \ - github.com/golangci/golangci-lint/cmd/golangci-lint \ - github.com/rakyll/statik -GOBIN ?= $(GOPATH)/bin -SHASUM := $(shell which sha256sum) +CORE_PACK := $(shell go list -m github.com/terra-project/core | sed 's/ /\@/g') export GO111MODULE = on # process build tags + build_tags = netgo ifeq ($(LEDGER_ENABLED),true) ifeq ($(OS),Windows_NT) @@ -22,11 +20,16 @@ ifeq ($(LEDGER_ENABLED),true) build_tags += ledger endif else - GCC = $(shell command -v gcc 2> /dev/null) - ifeq ($(GCC),) - $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S),OpenBSD) + $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) else - build_tags += ledger + GCC = $(shell command -v gcc 2> /dev/null) + ifeq ($(GCC),) + $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif endif endif endif @@ -34,90 +37,82 @@ endif ifeq ($(WITH_CLEVELDB),yes) build_tags += gcc endif +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) -# process linker flags +whitespace := +whitespace += $(whitespace) +comma := , +build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags)) -ldflags = -X github.com/terra-project/core/version.Version=$(VERSION) \ - -X github.com/terra-project/core/version.Commit=$(COMMIT) \ - -X "github.com/terra-project/core/version.BuildTags=$(build_tags)" \ +# process linker flags -ifneq ($(SHASUM),) - ldflags += -X github.com/terra-project/core/version.GoSumHash=$(shell sha256sum go.sum | cut -d ' ' -f1) -endif +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=terra \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=terrad \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=terracli \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" ifeq ($(WITH_CLEVELDB),yes) - build_tags += gcc + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb endif -build_tags += $(BUILD_TAGS) -build_tags := $(strip $(build_tags)) - ldflags += $(LDFLAGS) ldflags := $(strip $(ldflags)) BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' -######################################## -### All +# The below include contains the tools target. +include contrib/devtools/Makefile -all: clean go-mod-cache install lint test +all: install lint check -######################################## -### CI - -ci: get_tools install lint test +build: go.sum +ifeq ($(OS),Windows_NT) + go build -mod=readonly $(BUILD_FLAGS) -o build/terrad.exe ./cmd/terrad + go build -mod=readonly $(BUILD_FLAGS) -o build/terracli.exe ./cmd/terracli +else + go build -mod=readonly $(BUILD_FLAGS) -o build/terrad ./cmd/terrad + go build -mod=readonly $(BUILD_FLAGS) -o build/terracli ./cmd/terracli +endif -######################################## -### Build/Install +build-linux: go.sum + LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build -build: update_terra_lite_docs +build-contract-tests-hooks: ifeq ($(OS),Windows_NT) - go build $(BUILD_FLAGS) -o build/terrad.exe ./cmd/terrad - go build $(BUILD_FLAGS) -o build/terracli.exe ./cmd/terracli - go build $(BUILD_FLAGS) -o build/terrakeyutil.exe ./cmd/terrakeyutil + go build -mod=readonly $(BUILD_FLAGS) -o build/contract_tests.exe ./cmd/contract_tests else - go build $(BUILD_FLAGS) -o build/terrad ./cmd/terrad - go build $(BUILD_FLAGS) -o build/terracli ./cmd/terracli - go build $(BUILD_FLAGS) -o build/terrakeyutil ./cmd/terrakeyutil + go build -mod=readonly $(BUILD_FLAGS) -o build/contract_tests ./cmd/contract_tests endif -build-linux: - LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build +install: go.sum check-ledger + go install -mod=readonly $(BUILD_FLAGS) ./cmd/terrad + go install -mod=readonly $(BUILD_FLAGS) ./cmd/terracli -update_terra_lite_docs: - @statik -src=client/lcd/swagger-ui -dest=client/lcd -f +install-debug: go.sum + go install -mod=readonly $(BUILD_FLAGS) ./cmd/terradebug -install: update_terra_lite_docs - go install $(BUILD_FLAGS) ./cmd/terrad - go install $(BUILD_FLAGS) ./cmd/terracli - go install $(BUILD_FLAGS) ./cmd/terrakeyutil ######################################## ### Tools & dependencies -get_tools: - go get github.com/rakyll/statik - go get github.com/golangci/golangci-lint/cmd/golangci-lint - -update_tools: - @echo "--> Updating tools to correct version" - $(MAKE) --always-make get_tools - -go-mod-cache: go-sum +go-mod-cache: go.sum @echo "--> Download go modules to local cache" @go mod download -go-sum: get_tools +go.sum: go.mod @echo "--> Ensure dependencies have not been modified" @go mod verify -go-release: - @echo "--> Dry run for go-release" - BUILD_TAGS=$(shell echo \"$(build_tags)\") GOSUM=$(shell sha256sum go.sum | cut -d ' ' -f1) goreleaser release --skip-publish --rm-dist --debug +draw-deps: + @# requires brew install graphviz or apt-get install graphviz + go get github.com/RobotsAndPencils/goviz + @goviz -i ./cmd/terrad -d 2 | dot -Tpng -o dependency-graph.png clean: - rm -rf ./dist - rm -rf ./build + rm -rf snapcraft-local.yaml build/ distclean: clean rm -rf vendor/ @@ -125,30 +120,36 @@ distclean: clean ######################################## ### Testing -test: test_unit -test_unit: - @VERSION=$(VERSION) go test -mod=readonly $(PACKAGES_NOSIMULATION) -tags='ledger test_ledger_mock' +check: check-unit check-build +check-all: check check-race check-cover -test_race: - @VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION) +check-unit: + @VERSION=$(VERSION) go test -mod=readonly -tags='ledger test_ledger_mock' ./... -format: - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/terra-project/core +check-race: + @VERSION=$(VERSION) go test -mod=readonly -race -tags='ledger test_ledger_mock' ./... -benchmark: - @go test -bench=. $(PACKAGES_NOSIMULATION) +check-cover: + @go test -mod=readonly -timeout 30m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' ./... -lint: get_tools ci-lint -ci-lint: - @echo "--> Running lint..." +check-build: build + @go test -mod=readonly -p 4 `go list ./cli_test/...` -tags=cli_test -v + + +lint: golangci-lint golangci-lint run - go vet -composites=false -tests=false ./... find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s go mod verify +format: + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/cosmos/cosmos-sdk + +benchmark: + @go test -mod=readonly -bench=. ./... + ######################################## ### Local validator nodes using docker and docker-compose @@ -158,23 +159,41 @@ build-docker-terradnode: # Run a 4-node testnet locally localnet-start: localnet-stop - @if ! [ -f build/node0/terrad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/terrad:Z tendermint/terradnode testnet --v 5 -o . --starting-ip-address 192.168.10.2; fi - # replace docker ip to local port, mapped - sed -i -e 's/192.168.10.2:26656/localhost:26656/g; s/192.168.10.3:26656/localhost:26659/g; s/192.168.10.4:26656/localhost:26661/g; s/192.168.10.5:26656/localhost:26663/g' $(CURDIR)/build/node4/terrad/config/config.toml - # change allow duplicated ip option to prevent the error : cant not route ~ - sed -i -e 's/allow_duplicate_ip \= false/allow_duplicate_ip \= true/g' `find $(CURDIR)/build -name "config.toml"` + @if ! [ -f build/node0/terrad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/terrad:Z tendermint/terradnode testnet --v 4 -o . --starting-ip-address 192.168.10.2 ; fi docker-compose up -d # Stop testnet localnet-stop: docker-compose down -# To avoid unintended conflicts with file names, always add to .PHONY -# unless there is a reason not to. -# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: build install clean distclean update_terra_lite_docs \ -get_tools update_tools \ -test test_cli test_unit benchmark \ -build-linux build-docker-terradnode localnet-start localnet-stop \ -format update_dev_tools lint ci ci-lint\ -go-mod-cache go-sum +setup-contract-tests-data: + echo 'Prepare data for the contract tests' + rm -rf /tmp/contract_tests ; \ + mkdir /tmp/contract_tests ; \ + cp "${GOPATH}/pkg/mod/${CORE_PACK}/client/lcd/swagger-ui/swagger.yaml" /tmp/contract_tests/swagger.yaml ; \ + ./build/terrad init --home /tmp/contract_tests/.terrad --chain-id lcd contract-tests ; \ + tar -xzf lcd_test/testdata/state.tar.gz -C /tmp/contract_tests/ + +start-terra: setup-contract-tests-data + ./build/terrad --home /tmp/contract_tests/.terrad start & + @sleep 2s + +setup-transactions: start-terra + @bash ./lcd_test/testdata/setup.sh + +run-lcd-contract-tests: + @echo "Running Terra LCD for contract tests" + ./build/terracli rest-server --laddr tcp://0.0.0.0:8080 --home /tmp/contract_tests/.terracli --node http://localhost:26657 --chain-id lcd --trust-node true + +contract-tests: setup-transactions + @echo "Running Terra LCD for contract tests" + dredd && pkill terrad + +# include simulations +include sims.mk + +.PHONY: all build-linux install install-debug \ + go-mod-cache draw-deps clean build \ + setup-transactions setup-contract-tests-data start-terra run-lcd-contract-tests contract-tests \ + check check-all check-build check-cover check-ledger check-unit check-race + diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 84681e2b7..000000000 --- a/Vagrantfile +++ /dev/null @@ -1,51 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure("2") do |config| - config.vm.box = "ubuntu/xenial64" - - config.vm.provider "virtualbox" do |v| - v.memory = 4096 - v.cpus = 2 - end - - config.vm.provision "shell", inline: <<-SHELL - - # add docker repo - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" - - # and golang 1.9 support - # official repo doesn't have race detection runtime... - #add-apt-repository ppa:gophers/archive - add-apt-repository ppa:longsleep/golang-backports - - # install base requirements - apt-get update - apt-get install -y --no-install-recommends wget curl jq \ - make shellcheck bsdmainutils psmisc - apt-get install -y golang-1.9-go docker-ce - apt-get install -y language-pack-en - - # cleanup - apt-get autoremove -y - - # needed for docker - usermod -a -G docker vagrant - - # use "EOF" not EOF to avoid variable substitution of $PATH - echo 'export PATH=$PATH:/usr/lib/go-1.9/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile - echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile - echo 'export LC_ALL=en_US.UTF-8' >> /home/vagrant/.bash_profile - - mkdir -p /home/vagrant/go/bin - mkdir -p /home/vagrant/go/src/github.com/cosmos - ln -s /vagrant /home/vagrant/go/src/github.com/cosmos/cosmos-sdk - - chown -R vagrant:vagrant /home/vagrant/go - chown vagrant:vagrant /home/vagrant/.bash_profile - - su - vagrant -c 'source /home/vagrant/.bash_profile' - su - vagrant -c 'cd /home/vagrant/go/src/github.com/cosmos/cosmos-sdk && make get_tools' - SHELL -end diff --git a/app.test b/app.test new file mode 100755 index 000000000..8ac447ff9 Binary files /dev/null and b/app.test differ diff --git a/app/app.go b/app/app.go old mode 100755 new mode 100644 index 593306c89..216e5a7b0 --- a/app/app.go +++ b/app/app.go @@ -1,526 +1,256 @@ package app import ( - "fmt" "io" "os" - "sort" - "github.com/terra-project/core/types" - "github.com/terra-project/core/update" - "github.com/terra-project/core/version" - "github.com/terra-project/core/x/budget" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/pay" - "github.com/terra-project/core/x/treasury" - - "github.com/terra-project/core/types/assets" - - tdistr "github.com/terra-project/core/x/distribution" - tslashing "github.com/terra-project/core/x/slashing" - tstaking "github.com/terra-project/core/x/staking" + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/crisis" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + + "github.com/terra-project/core/x/auth" + "github.com/terra-project/core/x/bank" + "github.com/terra-project/core/x/crisis" + distr "github.com/terra-project/core/x/distribution" + "github.com/terra-project/core/x/genaccounts" + "github.com/terra-project/core/x/genutil" + "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/params" + "github.com/terra-project/core/x/slashing" + "github.com/terra-project/core/x/staking" + "github.com/terra-project/core/x/supply" + "github.com/terra-project/core/x/treasury" ) -const ( - appName = "TerraApp" - // DefaultKeyPass contains the default key password for genesis transactions - DefaultKeyPass = "12345678" -) +const appName = "TerraApp" -// default home directories for expected binaries var ( - DefaultCLIHome = os.ExpandEnv("$HOME/.terracli") + // default home directories for terracli + DefaultCLIHome = os.ExpandEnv("$HOME/.terracli") + + // default home directories for terrad DefaultNodeHome = os.ExpandEnv("$HOME/.terrad") + + // The ModuleBasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + genaccounts.AppModuleBasic{}, + genutil.AppModuleBasic{}, + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + distr.AppModuleBasic{}, + params.AppModuleBasic{}, + crisis.AppModuleBasic{}, + slashing.AppModuleBasic{}, + supply.AppModuleBasic{}, + oracle.AppModuleBasic{}, + market.AppModuleBasic{}, + treasury.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + auth.FeeCollectorName: nil, // just added to enable align fee + market.ModuleName: {supply.Minter, supply.Burner}, + oracle.ModuleName: nil, + distr.ModuleName: nil, + treasury.ModuleName: {supply.Minter}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + } ) -// TerraApp contains ABCI application +// custom tx codec +func MakeCodec() *codec.Codec { + var cdc = codec.New() + ModuleBasics.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + codec.RegisterEvidences(cdc) + return cdc +} + +// Extended ABCI application type TerraApp struct { *bam.BaseApp cdc *codec.Codec - assertInvariantsBlockly bool + invCheckPeriod uint // keys to access the substores - keyMain *sdk.KVStoreKey - keyAccount *sdk.KVStoreKey - keyStaking *sdk.KVStoreKey - tkeyStaking *sdk.TransientStoreKey - keySlashing *sdk.KVStoreKey - keyDistr *sdk.KVStoreKey - tkeyDistr *sdk.TransientStoreKey - keyFeeCollection *sdk.KVStoreKey - keyParams *sdk.KVStoreKey - tkeyParams *sdk.TransientStoreKey - keyOracle *sdk.KVStoreKey - keyTreasury *sdk.KVStoreKey - keyMarket *sdk.KVStoreKey - keyBudget *sdk.KVStoreKey - keyMint *sdk.KVStoreKey - - // Manage getting and setting accounts - accountKeeper auth.AccountKeeper - feeCollectionKeeper auth.FeeCollectionKeeper - bankKeeper bank.Keeper - stakingKeeper staking.Keeper - slashingKeeper slashing.Keeper - distrKeeper distr.Keeper - crisisKeeper crisis.Keeper - paramsKeeper params.Keeper - oracleKeeper oracle.Keeper - treasuryKeeper treasury.Keeper - marketKeeper market.Keeper - budgetKeeper budget.Keeper - mintKeeper mint.Keeper + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + + // keepers + accountKeeper auth.AccountKeeper + bankKeeper bank.Keeper + supplyKeeper supply.Keeper + stakingKeeper staking.Keeper + slashingKeeper slashing.Keeper + oracleKeeper oracle.Keeper + distrKeeper distr.Keeper + crisisKeeper crisis.Keeper + paramsKeeper params.Keeper + marketKeeper market.Keeper + treasuryKeeper treasury.Keeper + + // the module manager + mm *module.Manager } // NewTerraApp returns a reference to an initialized TerraApp. -func NewTerraApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest, assertInvariantsBlockly bool, baseAppOptions ...func(*bam.BaseApp)) *TerraApp { +func NewTerraApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, + invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *TerraApp { + cdc := MakeCodec() bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetAppVersion(version.Version) + + keys := sdk.NewKVStoreKeys( + bam.MainStoreKey, auth.StoreKey, staking.StoreKey, + supply.StoreKey, distr.StoreKey, slashing.StoreKey, + params.StoreKey, oracle.StoreKey, + market.StoreKey, treasury.StoreKey, + ) + tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) var app = &TerraApp{ - BaseApp: bApp, - cdc: cdc, - assertInvariantsBlockly: assertInvariantsBlockly, - keyMain: sdk.NewKVStoreKey(bam.MainStoreKey), - keyAccount: sdk.NewKVStoreKey(auth.StoreKey), - keyStaking: sdk.NewKVStoreKey(staking.StoreKey), - tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey), - keyDistr: sdk.NewKVStoreKey(distr.StoreKey), - tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey), - keySlashing: sdk.NewKVStoreKey(slashing.StoreKey), - keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey), - keyParams: sdk.NewKVStoreKey(params.StoreKey), - tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey), - keyOracle: sdk.NewKVStoreKey(oracle.StoreKey), - keyTreasury: sdk.NewKVStoreKey(treasury.StoreKey), - keyMarket: sdk.NewKVStoreKey(market.StoreKey), - keyBudget: sdk.NewKVStoreKey(budget.StoreKey), - keyMint: sdk.NewKVStoreKey(mint.StoreKey), + BaseApp: bApp, + cdc: cdc, + invCheckPeriod: invCheckPeriod, + keys: keys, + tkeys: tkeys, } - app.paramsKeeper = params.NewKeeper( - app.cdc, - app.keyParams, app.tkeyParams, - ) - - // define the accountKeeper - app.accountKeeper = auth.NewAccountKeeper( - app.cdc, - app.keyAccount, // target store - app.paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, // prototype - ) - // add handlers - app.feeCollectionKeeper = auth.NewFeeCollectionKeeper( - app.cdc, - app.keyFeeCollection, - ) - app.bankKeeper = bank.NewBaseKeeper( - app.accountKeeper, - app.paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - stakingKeeper := staking.NewKeeper( - app.cdc, - app.keyStaking, app.tkeyStaking, - app.bankKeeper, app.paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - app.distrKeeper = distr.NewKeeper( - app.cdc, - app.keyDistr, - app.paramsKeeper.Subspace(distr.DefaultParamspace), - app.bankKeeper, &stakingKeeper, app.feeCollectionKeeper, - distr.DefaultCodespace, - ) - app.slashingKeeper = slashing.NewKeeper( - app.cdc, - app.keySlashing, - &stakingKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), - slashing.DefaultCodespace, - ) - app.crisisKeeper = crisis.NewKeeper( - app.paramsKeeper.Subspace(crisis.DefaultParamspace), - app.distrKeeper, - app.bankKeeper, - app.feeCollectionKeeper, - ) - app.mintKeeper = mint.NewKeeper( - app.cdc, - app.keyMint, - stakingKeeper, - app.bankKeeper, - app.accountKeeper, - ) - app.oracleKeeper = oracle.NewKeeper( - app.cdc, - app.keyOracle, - app.mintKeeper, - app.distrKeeper, - app.feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - app.paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - app.marketKeeper = market.NewKeeper( - app.cdc, - app.keyMarket, - app.oracleKeeper, - app.mintKeeper, - app.paramsKeeper.Subspace(market.DefaultParamspace), - ) - app.treasuryKeeper = treasury.NewKeeper( - app.cdc, - app.keyTreasury, - stakingKeeper.GetValidatorSet(), - app.mintKeeper, - app.marketKeeper, - app.paramsKeeper.Subspace(treasury.DefaultParamspace), - ) - app.budgetKeeper = budget.NewKeeper( - app.cdc, - app.keyBudget, - app.marketKeeper, - app.mintKeeper, - app.treasuryKeeper, - stakingKeeper.GetValidatorSet(), - app.paramsKeeper.Subspace(budget.DefaultParamspace), - ) + // init params keeper and subspaces + app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) + authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace) + bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace) + stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace) + distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace) + slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace) + crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace) + oracleSubspace := app.paramsKeeper.Subspace(oracle.DefaultParamspace) + marketSubspace := app.paramsKeeper.Subspace(market.DefaultParamspace) + treasurySubspace := app.paramsKeeper.Subspace(treasury.DefaultParamspace) + + // add keepers + app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount) + app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs()) + app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms) + stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey], + app.supplyKeeper, stakingSubspace, staking.DefaultCodespace) + app.distrKeeper = distr.NewKeeper(app.cdc, keys[distr.StoreKey], distrSubspace, &stakingKeeper, + app.supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs()) + app.slashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, + slashingSubspace, slashing.DefaultCodespace) + app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName) + app.oracleKeeper = oracle.NewKeeper(app.cdc, keys[oracle.StoreKey], oracleSubspace, app.distrKeeper, + &stakingKeeper, app.supplyKeeper, distr.ModuleName, oracle.DefaultCodespace) + app.marketKeeper = market.NewKeeper(app.cdc, keys[market.StoreKey], marketSubspace, + app.oracleKeeper, app.supplyKeeper, market.DefaultCodespace) + app.treasuryKeeper = treasury.NewKeeper(app.cdc, keys[treasury.StoreKey], treasurySubspace, + app.supplyKeeper, app.marketKeeper, &stakingKeeper, app.distrKeeper, + oracle.ModuleName, distr.ModuleName, treasury.DefaultCodespace) // register the staking hooks - // NOTE: The stakingKeeper above is passed by reference, so that it can be - // modified like below: + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks app.stakingKeeper = *stakingKeeper.SetHooks( - NewStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) - - // register the crisis routes - bank.RegisterInvariants(&app.crisisKeeper, app.accountKeeper) - distr.RegisterInvariants(&app.crisisKeeper, app.distrKeeper, app.stakingKeeper) - staking.RegisterInvariants(&app.crisisKeeper, app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper) - - // register message routes - app.Router(). - AddRoute(bank.RouterKey, pay.NewHandler(app.bankKeeper, app.treasuryKeeper, app.feeCollectionKeeper)). - AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)). - AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)). - AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)). - AddRoute(oracle.RouterKey, oracle.NewHandler(app.oracleKeeper)). - AddRoute(budget.RouterKey, budget.NewHandler(app.budgetKeeper)). - AddRoute(market.RouterKey, market.NewHandler(app.marketKeeper)). - AddRoute(crisis.RouterKey, crisis.NewHandler(app.crisisKeeper)) - - app.QueryRouter(). - AddRoute(auth.QuerierRoute, auth.NewQuerier(app.accountKeeper)). - AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)). - AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)). - AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)). - AddRoute(treasury.QuerierRoute, treasury.NewQuerier(app.treasuryKeeper)). - AddRoute(market.QuerierRoute, market.NewQuerier(app.marketKeeper)). - AddRoute(oracle.QuerierRoute, oracle.NewQuerier(app.oracleKeeper)). - AddRoute(budget.QuerierRoute, budget.NewQuerier(app.budgetKeeper)) + staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks(), app.oracleKeeper.Hooks())) + + app.mm = module.NewManager( + genaccounts.NewAppModule(app.accountKeeper), + genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx), + auth.NewAppModule(app.accountKeeper), + bank.NewAppModule(app.bankKeeper, app.accountKeeper), + crisis.NewAppModule(&app.crisisKeeper), + supply.NewAppModule(app.supplyKeeper, app.accountKeeper), + distr.NewAppModule(app.distrKeeper, app.supplyKeeper), + slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), + staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper), + market.NewAppModule(app.marketKeeper), + oracle.NewAppModule(app.oracleKeeper), + treasury.NewAppModule(app.treasuryKeeper), + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) + + // After slashing actions, update prev day issuance of market module + app.mm.SetOrderEndBlockers(crisis.ModuleName, oracle.ModuleName, market.ModuleName, treasury.ModuleName, staking.ModuleName) + + // genutils must occur after staking so that pools are properly + // initialized with tokens from genesis accounts. + app.mm.SetOrderInitGenesis(genaccounts.ModuleName, distr.ModuleName, + staking.ModuleName, auth.ModuleName, bank.ModuleName, slashing.ModuleName, + oracle.ModuleName, market.ModuleName, treasury.ModuleName, + supply.ModuleName, crisis.ModuleName, genutil.ModuleName) + + app.mm.RegisterInvariants(&app.crisisKeeper) + app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) // initialize BaseApp - app.MountStores( - app.keyMain, app.keyAccount, app.keyStaking, app.keyDistr, - app.keySlashing, app.keyFeeCollection, app.keyParams, - app.tkeyParams, app.tkeyStaking, app.tkeyDistr, app.keyMarket, - app.keyOracle, app.keyTreasury, app.keyBudget, app.keyMint, - ) - app.SetInitChainer(app.initChainer) + app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) - app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper)) + app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.supplyKeeper, app.treasuryKeeper, auth.DefaultSigVerificationGasConsumer)) app.SetEndBlocker(app.EndBlocker) if loadLatest { - err := app.LoadLatestVersion(app.keyMain) + err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) if err != nil { cmn.Exit(err.Error()) } } - return app } -// Query overides query function in baseapp to change result of "/app/version" query. -func (app *TerraApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - - if req.Path == "/app/version" { - return abci.ResponseQuery{ - Code: uint32(sdk.CodeOK), - Codespace: string(sdk.CodespaceRoot), - Value: []byte(version.Version), - } - } - - return app.BaseApp.Query(req) -} - -// MakeCodec builds a custom tx codec -func MakeCodec() *codec.Codec { - var cdc = codec.New() - - // left codec for backward compatibility - pay.RegisterCodec(cdc) - tstaking.RegisterCodec(cdc) - tdistr.RegisterCodec(cdc) - tslashing.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - types.RegisterCodec(cdc) - oracle.RegisterCodec(cdc) - budget.RegisterCodec(cdc) - market.RegisterCodec(cdc) - treasury.RegisterCodec(cdc) - crisis.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - stakingtypes.SetMsgCodec(cdc) - distrtypes.SetMsgCodec(cdc) - bank.SetMsgCodec(cdc) - slashing.SetMsgCodec(cdc) - - return cdc -} - -// BeginBlocker application updates every end block +// application updates every begin block func (app *TerraApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - - // distribute rewards for the previous block - distr.BeginBlocker(ctx, req, app.distrKeeper) - - // slash anyone who double signed. - // NOTE: This should happen after distr.BeginBlocker so that - // there is nothing left over in the validator fee pool, - // so as to keep the CanWithdrawInvariant invariant. - // TODO: This should really happen at EndBlocker. - tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper) - - return abci.ResponseBeginBlock{ - Tags: tags.ToKVPairs(), - } + return app.mm.BeginBlock(ctx, req) } -// EndBlocker application updates every end block +// application updates every end block func (app *TerraApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - validatorUpdates, tags := staking.EndBlocker(ctx, app.stakingKeeper) - - oracleTags := oracle.EndBlocker(ctx, app.oracleKeeper) - tags = append(tags, oracleTags...) - - budgetTags := budget.EndBlocker(ctx, app.budgetKeeper) - tags = append(tags, budgetTags...) - - treasuryTags := treasury.EndBlocker(ctx, app.treasuryKeeper) - tags = append(tags, treasuryTags...) - - updateTags := update.EndBlocker(ctx, app.accountKeeper, app.oracleKeeper, app.marketKeeper) - tags = append(tags, updateTags...) - - if app.assertInvariantsBlockly { - app.assertRuntimeInvariants() - } - - return abci.ResponseEndBlock{ - ValidatorUpdates: validatorUpdates, - Tags: tags, - } + return app.mm.EndBlock(ctx, req) } -// initialize store from a genesis state -func (app *TerraApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisState) []abci.ValidatorUpdate { - genesisState.Sanitize() - - // load the accounts - for _, gacc := range genesisState.Accounts { - acc := gacc.ToAccount() - acc = app.accountKeeper.NewAccount(ctx, acc) // set account number - app.accountKeeper.SetAccount(ctx, acc) - } - - // initialize distribution (must happen before staking) - distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData) - - // load the initial staking information - validators, err := staking.InitGenesis(ctx, app.stakingKeeper, genesisState.StakingData) - if err != nil { - panic(err) // TODO find a way to do this w/o panics - } - - // initialize module-specific stores - auth.InitGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper, genesisState.AuthData) - bank.InitGenesis(ctx, app.bankKeeper, genesisState.BankData) - slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData.Validators.ToSDKValidators()) - crisis.InitGenesis(ctx, app.crisisKeeper, genesisState.CrisisData) - treasury.InitGenesis(ctx, app.treasuryKeeper, genesisState.TreasuryData) - market.InitGenesis(ctx, app.marketKeeper, genesisState.MarketData) - budget.InitGenesis(ctx, app.budgetKeeper, genesisState.BudgetData) - oracle.InitGenesis(ctx, app.oracleKeeper, genesisState.OracleData) - - // validate genesis state - if err := TerraValidateGenesisState(genesisState); err != nil { - panic(err) // TODO find a way to do this w/o panics - } - - if len(genesisState.GenTxs) > 0 { - for _, genTx := range genesisState.GenTxs { - var tx auth.StdTx - err = app.cdc.UnmarshalJSON(genTx, &tx) - if err != nil { - panic(err) - } - - bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx) - res := app.BaseApp.DeliverTx(bz) - if !res.IsOK() { - panic(res.Log) - } - } - - validators = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - } - return validators +// application update at chain initialization +func (app *TerraApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState simapp.GenesisState + app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState) + return app.mm.InitGenesis(ctx, genesisState) } -// custom logic for Terra initialization -func (app *TerraApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - stateJSON := req.AppStateBytes - // TODO is this now the whole genesis file? - - var genesisState GenesisState - err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) - if err != nil { - panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 - // return sdk.ErrGenesisParse("").TraceCause(err, "") - } - - validators := app.initFromGenesisState(ctx, genesisState) - - // sanity check - if len(req.Validators) > 0 { - if len(req.Validators) != len(validators) { - panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d)", - len(req.Validators), len(validators))) - } - sort.Sort(abci.ValidatorUpdates(req.Validators)) - sort.Sort(abci.ValidatorUpdates(validators)) - for i, val := range validators { - if !val.Equal(req.Validators[i]) { - panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i)) - } - } - } - - // GetIssuance needs to be called once to read account balances to the store - app.mintKeeper.GetIssuance(ctx, assets.MicroLunaDenom, sdk.ZeroInt()) - - // assert runtime invariants - app.assertRuntimeInvariants() - - return abci.ResponseInitChain{ - Validators: validators, - } -} - -// LoadHeight loads a particular height +// load a particular height func (app *TerraApp) LoadHeight(height int64) error { - return app.LoadVersion(height, app.keyMain) -} - -//______________________________________________________________________________________________ - -var _ sdk.StakingHooks = StakingHooks{} - -// StakingHooks contains combined distribution and slashing hooks needed for the -// staking module. -type StakingHooks struct { - dh distr.Hooks - sh slashing.Hooks -} - -// NewStakingHooks nolint -func NewStakingHooks(dh distr.Hooks, sh slashing.Hooks) StakingHooks { - return StakingHooks{dh, sh} -} - -// AfterValidatorCreated nolint -func (h StakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { - h.dh.AfterValidatorCreated(ctx, valAddr) - h.sh.AfterValidatorCreated(ctx, valAddr) + return app.LoadVersion(height, app.keys[bam.MainStoreKey]) } -// BeforeValidatorModified nolint -func (h StakingHooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { - h.dh.BeforeValidatorModified(ctx, valAddr) - h.sh.BeforeValidatorModified(ctx, valAddr) -} - -// AfterValidatorRemoved nolint -func (h StakingHooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { - h.dh.AfterValidatorRemoved(ctx, consAddr, valAddr) - h.sh.AfterValidatorRemoved(ctx, consAddr, valAddr) -} - -// AfterValidatorBonded nolint -func (h StakingHooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { - h.dh.AfterValidatorBonded(ctx, consAddr, valAddr) - h.sh.AfterValidatorBonded(ctx, consAddr, valAddr) -} - -// AfterValidatorBeginUnbonding nolint -func (h StakingHooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { - h.dh.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr) - h.sh.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr) -} - -// BeforeDelegationCreated nolint -func (h StakingHooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { - h.dh.BeforeDelegationCreated(ctx, delAddr, valAddr) - h.sh.BeforeDelegationCreated(ctx, delAddr, valAddr) -} - -// BeforeDelegationSharesModified nolint -func (h StakingHooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { - h.dh.BeforeDelegationSharesModified(ctx, delAddr, valAddr) - h.sh.BeforeDelegationSharesModified(ctx, delAddr, valAddr) -} - -// BeforeDelegationRemoved nolint -func (h StakingHooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { - h.dh.BeforeDelegationRemoved(ctx, delAddr, valAddr) - h.sh.BeforeDelegationRemoved(ctx, delAddr, valAddr) -} - -// AfterDelegationModified nolint -func (h StakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { - h.dh.AfterDelegationModified(ctx, delAddr, valAddr) - h.sh.AfterDelegationModified(ctx, delAddr, valAddr) -} +// ModuleAccountAddrs returns all the app's module account addresses. +func (app *TerraApp) ModuleAccountAddrs() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[supply.NewModuleAddress(acc).String()] = true + } -// BeforeValidatorSlashed nolint -func (h StakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) { - h.dh.BeforeValidatorSlashed(ctx, valAddr, fraction) - h.sh.BeforeValidatorSlashed(ctx, valAddr, fraction) + return modAccAddrs } diff --git a/app/app_test.go b/app/app_test.go new file mode 100644 index 000000000..a4a17f0a4 --- /dev/null +++ b/app/app_test.go @@ -0,0 +1,54 @@ +package app + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + + abci "github.com/tendermint/tendermint/abci/types" +) + +func TestTerraExport(t *testing.T) { + db := dbm.NewMemDB() + tapp := NewTerraApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0) + setGenesis(tapp) + + // Making a new app object with the db, so that initchain hasn't been called + newTapp := NewTerraApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0) + _, _, err := newTapp.ExportAppStateAndValidators(false, []string{}) + require.NoError(t, err, "ExportAppStateAndValidators should not have an error") +} + +func setGenesis(tapp *TerraApp) error { + + genesisState := ModuleBasics.DefaultGenesis() + stateBytes, err := codec.MarshalJSONIndent(tapp.cdc, genesisState) + if err != nil { + return err + } + + // Initialize the chain + tapp.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, + }, + ) + tapp.Commit() + return nil +} + +// ensure that black listed addresses are properly set in bank keeper +func TestBlackListedAddrs(t *testing.T) { + db := dbm.NewMemDB() + app := NewTerraApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0) + + for acc := range maccPerms { + require.True(t, app.bankKeeper.BlacklistedAddr(app.supplyKeeper.GetModuleAddress(acc))) + } +} diff --git a/app/export.go b/app/export.go index c908ab30e..be2f7b994 100644 --- a/app/export.go +++ b/app/export.go @@ -4,26 +4,19 @@ import ( "encoding/json" "log" - "github.com/terra-project/core/x/budget" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/treasury" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/crisis" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" + + "github.com/terra-project/core/x/slashing" + "github.com/terra-project/core/x/staking" ) -// ExportAppStateAndValidators exports the state of Terra for a genesis file -func (app *TerraApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string) ( - appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { +// ExportAppStateAndValidators export the state of terra for a genesis file +func (app *TerraApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string, +) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { // as if they could withdraw from the start of the next block ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) @@ -32,28 +25,7 @@ func (app *TerraApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLi app.prepForZeroHeightGenesis(ctx, jailWhiteList) } - // iterate to get the accounts - accounts := []GenesisAccount{} - appendAccount := func(acc auth.Account) (stop bool) { - account := NewGenesisAccountI(acc) - accounts = append(accounts, account) - return false - } - app.accountKeeper.IterateAccounts(ctx, appendAccount) - - genState := NewGenesisState( - accounts, - auth.ExportGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper), - bank.ExportGenesis(ctx, app.bankKeeper), - staking.ExportGenesis(ctx, app.stakingKeeper), - distr.ExportGenesis(ctx, app.distrKeeper), - oracle.ExportGenesis(ctx, app.oracleKeeper), - budget.ExportGenesis(ctx, app.budgetKeeper), - crisis.ExportGenesis(ctx, app.crisisKeeper), - treasury.ExportGenesis(ctx, app.treasuryKeeper), - slashing.ExportGenesis(ctx, app.slashingKeeper), - market.ExportGenesis(ctx, app.marketKeeper), - ) + genState := app.mm.ExportGenesis(ctx) appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err @@ -62,7 +34,9 @@ func (app *TerraApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLi return appState, validators, nil } -// prepare for fresh start at zero height +// prepForZeroHeightGenesis prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) { applyWhiteList := false @@ -82,12 +56,12 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s } /* Just to be safe, assert the invariants on current state. */ - app.assertRuntimeInvariantsOnContext(ctx) + app.crisisKeeper.AssertInvariants(ctx) /* Handle fee distribution state. */ // withdraw all validator commission - app.stakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) { + app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) { _, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) return false }) @@ -109,7 +83,7 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s ctx = ctx.WithBlockHeight(0) // reinitialize all validators - app.stakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) { + app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) { // donate any unwithdrawn outstanding reward fraction tokens to the community pool scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator()) @@ -152,10 +126,11 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s // Iterate through validators by power descending, reset bond heights, and // update bond intra-tx counters. - store := ctx.KVStore(app.keyStaking) + store := ctx.KVStore(app.keys[staking.StoreKey]) iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey) counter := int16(0) + var valConsAddrs []sdk.ConsAddress for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(iter.Key()[1:]) validator, found := app.stakingKeeper.GetValidator(ctx, addr) @@ -164,6 +139,7 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s } validator.UnbondingHeight = 0 + valConsAddrs = append(valConsAddrs, validator.ConsAddress()) if applyWhiteList && !whiteListMap[addr.String()] { validator.Jailed = true } @@ -187,11 +163,4 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s return false }, ) - - // reset submit height on program infos - app.budgetKeeper.IteratePrograms(ctx, false, func(program budget.Program) (stop bool) { - program.SubmitBlock = 0 - app.budgetKeeper.StoreProgram(ctx, program) - return false - }) } diff --git a/app/genesis.go b/app/genesis.go deleted file mode 100644 index 0d2e48c6f..000000000 --- a/app/genesis.go +++ /dev/null @@ -1,474 +0,0 @@ -package app - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/budget" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/treasury" - - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/crisis" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// GenesisState is a state to Unmarshal -type GenesisState struct { - Accounts []GenesisAccount `json:"accounts"` - AuthData auth.GenesisState `json:"auth"` - BankData bank.GenesisState `json:"bank"` - StakingData staking.GenesisState `json:"staking"` - DistrData distr.GenesisState `json:"distr"` - TreasuryData treasury.GenesisState `json:"treasury"` - BudgetData budget.GenesisState `json:"budget"` - OracleData oracle.GenesisState `json:"oracle"` - CrisisData crisis.GenesisState `json:"crisis"` - SlashingData slashing.GenesisState `json:"slashing"` - MarketData market.GenesisState `json:"market"` - GenTxs []json.RawMessage `json:"gentxs"` -} - -// NewGenesisState returns new genesis state -func NewGenesisState(accounts []GenesisAccount, - authData auth.GenesisState, - bankData bank.GenesisState, - stakingData staking.GenesisState, - distrData distr.GenesisState, - oracleData oracle.GenesisState, - budgetData budget.GenesisState, - crisisData crisis.GenesisState, - treasuryData treasury.GenesisState, - slashingData slashing.GenesisState, - marketData market.GenesisState) GenesisState { - - return GenesisState{ - Accounts: accounts, - AuthData: authData, - BankData: bankData, - StakingData: stakingData, - DistrData: distrData, - OracleData: oracleData, - CrisisData: crisisData, - TreasuryData: treasuryData, - BudgetData: budgetData, - SlashingData: slashingData, - MarketData: marketData, - } -} - -// Sanitize sorts accounts and coin sets. -func (gs GenesisState) Sanitize() { - sort.Slice(gs.Accounts, func(i, j int) bool { - return gs.Accounts[i].AccountNumber < gs.Accounts[j].AccountNumber - }) - - for _, acc := range gs.Accounts { - acc.Coins = acc.Coins.Sort() - } -} - -// GenesisAccount defines an account initialized at genesis. -type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - Sequence uint64 `json:"sequence_number"` - AccountNumber uint64 `json:"account_number"` - - // vesting account fields - OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization - DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation - DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation - StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time) - EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time) - VestingSchedules []types.VestingSchedule `json:"vesting_schedules"` // vesting schedule (clif: UNIX Epoch time, ratio: dec) - LazyVestingSchedules []types.LazyVestingSchedule `json:"lazy_vesting_schedules"` // lazy vesting schedule (start_time&end_time: UNIX Epoch time, ratio: dec) -} - -// NewGenesisAccount returns new genesis account -func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { - return GenesisAccount{ - Address: acc.Address, - Coins: acc.Coins, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, - } -} - -// NewGenesisAccountI no-lint -func NewGenesisAccountI(acc auth.Account) GenesisAccount { - gacc := GenesisAccount{ - Address: acc.GetAddress(), - Coins: acc.GetCoins(), - AccountNumber: acc.GetAccountNumber(), - Sequence: acc.GetSequence(), - } - - vacc, ok := acc.(auth.VestingAccount) - if ok { - gacc.OriginalVesting = vacc.GetOriginalVesting() - gacc.DelegatedFree = vacc.GetDelegatedFree() - gacc.DelegatedVesting = vacc.GetDelegatedVesting() - gacc.StartTime = vacc.GetStartTime() - gacc.EndTime = vacc.GetEndTime() - - gvacc, ok := vacc.(types.GradedVestingAccount) - if ok { - gacc.VestingSchedules = gvacc.GetVestingSchedules() - } - - lgvacc, ok := vacc.(types.LazyGradedVestingAccount) - if ok { - gacc.LazyVestingSchedules = lgvacc.GetLazyVestingSchedules() - } - } - - return gacc -} - -// ToAccount converts GenesisAccount to auth.BaseAccount -func (ga *GenesisAccount) ToAccount() auth.Account { - bacc := &auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), - AccountNumber: ga.AccountNumber, - Sequence: ga.Sequence, - } - - if !ga.OriginalVesting.IsZero() { - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: bacc, - OriginalVesting: ga.OriginalVesting, - DelegatedFree: ga.DelegatedFree, - DelegatedVesting: ga.DelegatedVesting, - EndTime: ga.EndTime, - } - - if ga.StartTime != 0 && ga.EndTime != 0 { - return &auth.ContinuousVestingAccount{ - BaseVestingAccount: baseVestingAcc, - StartTime: ga.StartTime, - } - } else if ga.EndTime != 0 { - return &auth.DelayedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - } - } else if ga.VestingSchedules != nil { - return &types.BaseGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - VestingSchedules: ga.VestingSchedules, - } - } else if ga.LazyVestingSchedules != nil { - return &types.BaseLazyGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - LazyVestingSchedules: ga.LazyVestingSchedules, - } - } - } - - return bacc -} - -// TerraAppGenState creates the core parameters for genesis initialization for Terra -// note that the pubkey input is this machines pubkey -func TerraAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( - genesisState GenesisState, err error) { - - if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { - return genesisState, err - } - - // if there are no gen txs to be processed, return the default empty state - if len(appGenTxs) == 0 { - return genesisState, errors.New("there must be at least one genesis tx") - } - - stakingData := genesisState.StakingData - for i, genTx := range appGenTxs { - var tx auth.StdTx - if err := cdc.UnmarshalJSON(genTx, &tx); err != nil { - return genesisState, err - } - - msgs := tx.GetMsgs() - if len(msgs) != 1 { - return genesisState, errors.New( - "must provide genesis StdTx with exactly 1 CreateValidator message") - } - - if _, ok := msgs[0].(staking.MsgCreateValidator); !ok { - return genesisState, fmt.Errorf( - "Genesis transaction %v does not contain a MsgCreateValidator", i) - } - } - - for _, acc := range genesisState.Accounts { - for _, coin := range acc.Coins { - if coin.Denom == genesisState.StakingData.Params.BondDenom { - stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens. - Add(coin.Amount) // increase the supply - } - } - } - - genesisState.StakingData = stakingData - genesisState.GenTxs = appGenTxs - - return genesisState, nil -} - -// NewDefaultGenesisState generates the default state for Terra. -func NewDefaultGenesisState() GenesisState { - - // remove community tax - distrGenState := distr.DefaultGenesisState() - distrGenState.CommunityTax = sdk.ZeroDec() - - stakingGenState := staking.DefaultGenesisState() - stakingGenState.Params.BondDenom = assets.MicroLunaDenom - stakingGenState.Params.MaxValidators = 100 - - return GenesisState{ - Accounts: nil, - AuthData: auth.DefaultGenesisState(), - StakingData: stakingGenState, - DistrData: distrGenState, - BankData: bank.DefaultGenesisState(), - BudgetData: budget.DefaultGenesisState(), - OracleData: oracle.DefaultGenesisState(), - TreasuryData: treasury.DefaultGenesisState(), - CrisisData: crisis.DefaultGenesisState(), - SlashingData: slashing.DefaultGenesisState(), - MarketData: market.DefaultGenesisState(), - GenTxs: nil, - } -} - -// TerraValidateGenesisState ensures that the genesis state obeys the expected invariants -// TODO: No validators are both bonded and jailed (#2088) -// TODO: Error if there is a duplicate validator (#1708) -// TODO: Ensure all state machine parameters are in genesis (#1704) -func TerraValidateGenesisState(genesisState GenesisState) error { - if err := validateGenesisStateAccounts(genesisState.Accounts); err != nil { - return err - } - - // skip stakingData validation as genesis is created from txs - if len(genesisState.GenTxs) > 0 { - return nil - } - - if err := auth.ValidateGenesis(genesisState.AuthData); err != nil { - return err - } - if err := bank.ValidateGenesis(genesisState.BankData); err != nil { - return err - } - if err := staking.ValidateGenesis(genesisState.StakingData); err != nil { - return err - } - if err := distr.ValidateGenesis(genesisState.DistrData); err != nil { - return err - } - if err := slashing.ValidateGenesis(genesisState.SlashingData); err != nil { - return err - } - if err := crisis.ValidateGenesis(genesisState.CrisisData); err != nil { - return err - } - - return market.ValidateGenesis(genesisState.MarketData) -} - -// validateGenesisStateAccounts performs validation of genesis accounts. It -// ensures that there are no duplicate accounts in the genesis state and any -// provided vesting accounts are valid. -func validateGenesisStateAccounts(accs []GenesisAccount) error { - addrMap := make(map[string]bool, len(accs)) - for _, acc := range accs { - addrStr := acc.Address.String() - - // disallow any duplicate accounts - if _, ok := addrMap[addrStr]; ok { - return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) - } - - // validate any vesting fields - if !acc.OriginalVesting.IsZero() { - - if acc.VestingSchedules != nil && len(acc.VestingSchedules) > 0 { - for _, vestingSchedule := range acc.VestingSchedules { - if !vestingSchedule.IsValid() { - return fmt.Errorf("schedule is invalid for vesting account; address: %s, denom: %s", addrStr, vestingSchedule.GetDenom()) - } - } - } else if acc.LazyVestingSchedules != nil && len(acc.LazyVestingSchedules) > 0 { - for _, lazyVestingSchedule := range acc.LazyVestingSchedules { - if !lazyVestingSchedule.IsValid() { - return fmt.Errorf("lazy schedule is invalid for vesting account; address: %s, denom: %s", addrStr, lazyVestingSchedule.GetDenom()) - } - } - } else { - if acc.EndTime == 0 { - return fmt.Errorf("missing end time for vesting account; address: %s", addrStr) - } - - if acc.StartTime >= acc.EndTime { - return fmt.Errorf( - "vesting start time must before end time; address: %s, start: %s, end: %s", - addrStr, - time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339), - time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339), - ) - } - } - - } - - addrMap[addrStr] = true - } - - return nil -} - -// TerraAppGenStateJSON returns genesis state with JSON -func TerraAppGenStateJSON(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( - appState json.RawMessage, err error) { - // create the final app state - genesisState, err := TerraAppGenState(cdc, genDoc, appGenTxs) - if err != nil { - return nil, err - } - return codec.MarshalJSONIndent(cdc, genesisState) -} - -// CollectStdTxs processes and validates application's genesis StdTxs and returns -// the list of appGenTxs, and persistent peers required to generate genesis.json. -func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) ( - appGenTxs []auth.StdTx, persistentPeers string, err error) { - - var fos []os.FileInfo - fos, err = ioutil.ReadDir(genTxsDir) - if err != nil { - return appGenTxs, persistentPeers, err - } - - // prepare a map of all accounts in genesis state to then validate - // against the validators addresses - var appState GenesisState - if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { - return appGenTxs, persistentPeers, err - } - - addrMap := make(map[string]GenesisAccount, len(appState.Accounts)) - for i := 0; i < len(appState.Accounts); i++ { - acc := appState.Accounts[i] - addrMap[acc.Address.String()] = acc - } - - // addresses and IPs (and port) validator server info - var addressesIPs []string - - for _, fo := range fos { - filename := filepath.Join(genTxsDir, fo.Name()) - if !fo.IsDir() && (filepath.Ext(filename) != ".json") { - continue - } - - // get the genStdTx - var jsonRawTx []byte - if jsonRawTx, err = ioutil.ReadFile(filename); err != nil { - return appGenTxs, persistentPeers, err - } - var genStdTx auth.StdTx - if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil { - return appGenTxs, persistentPeers, err - } - appGenTxs = append(appGenTxs, genStdTx) - - // the memo flag is used to store - // the ip and node-id, for example this may be: - // "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656" - nodeAddrIP := genStdTx.GetMemo() - if len(nodeAddrIP) == 0 { - return appGenTxs, persistentPeers, fmt.Errorf( - "couldn't find node's address and IP in %s", fo.Name()) - } - - // genesis transactions must be single-message - msgs := genStdTx.GetMsgs() - if len(msgs) != 1 { - - return appGenTxs, persistentPeers, errors.New( - "each genesis transaction must provide a single genesis message") - } - - msg := msgs[0].(staking.MsgCreateValidator) - // validate delegator and validator addresses and funds against the accounts in the state - delAddr := msg.DelegatorAddress.String() - valAddr := sdk.AccAddress(msg.ValidatorAddress).String() - - delAcc, delOk := addrMap[delAddr] - _, valOk := addrMap[valAddr] - - accsNotInGenesis := []string{} - if !delOk { - accsNotInGenesis = append(accsNotInGenesis, delAddr) - } - if !valOk { - accsNotInGenesis = append(accsNotInGenesis, valAddr) - } - if len(accsNotInGenesis) != 0 { - return appGenTxs, persistentPeers, fmt.Errorf( - "account(s) %v not in genesis.json: %+v", strings.Join(accsNotInGenesis, " "), addrMap) - } - - if delAcc.Coins.AmountOf(msg.Value.Denom).LT(msg.Value.Amount) { - return appGenTxs, persistentPeers, fmt.Errorf( - "insufficient fund for delegation %v: %v < %v", - delAcc.Address, delAcc.Coins.AmountOf(msg.Value.Denom), msg.Value.Amount, - ) - } - - // exclude itself from persistent peers - if msg.Description.Moniker != moniker { - addressesIPs = append(addressesIPs, nodeAddrIP) - } - } - - sort.Strings(addressesIPs) - persistentPeers = strings.Join(addressesIPs, ",") - - return appGenTxs, persistentPeers, nil -} - -// NewDefaultGenesisAccount returns default genesis account -func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { - accAuth := auth.NewBaseAccountWithAddress(addr) - coins := sdk.Coins{ - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1000).MulRaw(assets.MicroUnit)), - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(100).MulRaw(assets.MicroUnit)), - } - - coins.Sort() - - accAuth.Coins = coins - return NewGenesisAccount(&accAuth) -} diff --git a/app/invariants.go b/app/invariants.go deleted file mode 100644 index 32373b07b..000000000 --- a/app/invariants.go +++ /dev/null @@ -1,30 +0,0 @@ -package app - -import ( - "fmt" - "time" - - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (app *TerraApp) assertRuntimeInvariants() { - ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1}) - app.assertRuntimeInvariantsOnContext(ctx) -} - -func (app *TerraApp) assertRuntimeInvariantsOnContext(ctx sdk.Context) { - start := time.Now() - invarRoutes := app.crisisKeeper.Routes() - for _, ir := range invarRoutes { - if err := ir.Invar(ctx); err != nil { - panic(fmt.Errorf("invariant broken: %s\n"+ - "\tCRITICAL please submit the following transaction:\n"+ - "\t\t terracli tx crisis invariant-broken %v %v", err, ir.ModuleName, ir.Route)) - } - } - end := time.Now() - diff := end.Sub(start) - app.BaseApp.Logger().With("module", "invariants").Info("Asserted all invariants", "duration", diff) -} diff --git a/app/params.go b/app/params.go new file mode 100644 index 000000000..204764fae --- /dev/null +++ b/app/params.go @@ -0,0 +1,25 @@ +package app + +// Simulation parameter constants +const ( + StakePerAccount = "stake_per_account" + InitiallyBondedValidators = "initially_bonded_validators" + OpWeightDeductFee = "op_weight_deduct_fee" + OpWeightMsgSend = "op_weight_msg_send" + OpWeightSingleInputMsgMultiSend = "op_weight_single_input_msg_multisend" + OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address" + OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward" + OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission" + OpWeightSubmitVotingSlashingTextProposal = "op_weight_submit_voting_slashing_text_proposal" + OpWeightSubmitVotingSlashingCommunitySpendProposal = "op_weight_submit_voting_slashing_community_spend_proposal" + OpWeightSubmitVotingSlashingParamChangeProposal = "op_weight_submit_voting_slashing_param_change_proposal" + OpWeightSubmitVotingSlashingTaxRateUpdateProposal = "op_weight_submit_voting_slashing_tax_rate_update_proposal" + OpWeightSubmitVotingSlashingRewardWeightUpdateProposal = "op_weight_submit_voting_slashing_reward_weight_update_proposal" + OpWeightMsgDeposit = "op_weight_msg_deposit" + OpWeightMsgCreateValidator = "op_weight_msg_create_validator" + OpWeightMsgEditValidator = "op_weight_msg_edit_validator" + OpWeightMsgDelegate = "op_weight_msg_delegate" + OpWeightMsgUndelegate = "op_weight_msg_undelegate" + OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate" + OpWeightMsgUnjail = "op_weight_msg_unjail" +) diff --git a/app/sim_test.go b/app/sim_test.go new file mode 100644 index 000000000..d3bd698c3 --- /dev/null +++ b/app/sim_test.go @@ -0,0 +1,814 @@ +package app + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "math/rand" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation" + "github.com/cosmos/cosmos-sdk/x/supply" + + core "github.com/terra-project/core/types" + authsim "github.com/terra-project/core/x/auth/simulation" + "github.com/terra-project/core/x/market" + marketsim "github.com/terra-project/core/x/market/simulation" + "github.com/terra-project/core/x/oracle" + oraclesim "github.com/terra-project/core/x/oracle/simulation" + "github.com/terra-project/core/x/treasury" +) + +func init() { + flag.StringVar(&genesisFile, "Genesis", "", "custom simulation genesis file; cannot be used with params file") + flag.StringVar(¶msFile, "Params", "", "custom simulation params file which overrides any random params; cannot be used with genesis") + flag.StringVar(&exportParamsPath, "ExportParamsPath", "", "custom file path to save the exported params JSON") + flag.IntVar(&exportParamsHeight, "ExportParamsHeight", 0, "height to which export the randomly generated params") + flag.StringVar(&exportStatePath, "ExportStatePath", "", "custom file path to save the exported app state JSON") + flag.StringVar(&exportStatsPath, "ExportStatsPath", "", "custom file path to save the exported simulation statistics JSON") + flag.Int64Var(&seed, "Seed", 42, "simulation random seed") + flag.IntVar(&initialBlockHeight, "InitialBlockHeight", 1, "initial block to start the simulation") + flag.IntVar(&numBlocks, "NumBlocks", 500, "number of new blocks to simulate from the initial block height") + flag.IntVar(&blockSize, "BlockSize", 200, "operations per block") + flag.BoolVar(&enabled, "Enabled", false, "enable the simulation") + flag.BoolVar(&verbose, "Verbose", false, "verbose log output") + flag.BoolVar(&lean, "Lean", false, "lean simulation log output") + flag.BoolVar(&commit, "Commit", false, "have the simulation commit") + flag.IntVar(&period, "Period", 1, "run slow invariants only once every period assertions") + flag.BoolVar(&onOperation, "SimulateEveryOperation", false, "run slow invariants every operation") + flag.BoolVar(&allInvariants, "PrintAllInvariants", false, "print all invariants if a broken invariant is found") + flag.Int64Var(&genesisTime, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time") + + config := sdk.GetConfig() + config.SetCoinType(core.CoinType) + config.SetFullFundraiserPath(core.FullFundraiserPath) + config.SetBech32PrefixForAccount(core.Bech32PrefixAccAddr, core.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(core.Bech32PrefixValAddr, core.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(core.Bech32PrefixConsAddr, core.Bech32PrefixConsPub) + config.Seal() +} + +// helper function for populating input for SimulateFromSeed +func getSimulateFromSeedInput(tb testing.TB, w io.Writer, app *TerraApp) ( + testing.TB, io.Writer, *baseapp.BaseApp, simulation.AppStateFn, int64, + simulation.WeightedOperations, sdk.Invariants, int, int, int, int, string, + bool, bool, bool, bool, bool, map[string]bool) { + + exportParams := exportParamsPath != "" + + return tb, w, app.BaseApp, appStateFn, seed, + testAndRunTxs(app), invariants(app), + initialBlockHeight, numBlocks, exportParamsHeight, blockSize, + exportStatsPath, exportParams, commit, lean, onOperation, allInvariants, app.ModuleAccountAddrs() +} + +func appStateFn( + r *rand.Rand, accs []simulation.Account, +) (appState json.RawMessage, simAccs []simulation.Account, chainID string, genesisTimestamp time.Time) { + + cdc := MakeCodec() + + if genesisTime == 0 { + genesisTimestamp = simulation.RandTimestamp(r) + } else { + genesisTimestamp = time.Unix(genesisTime, 0) + } + + switch { + case paramsFile != "" && genesisFile != "": + panic("cannot provide both a genesis file and a params file") + + case genesisFile != "": + appState, simAccs, chainID = simapp.AppStateFromGenesisFileFn(r, accs, genesisTimestamp) + + case paramsFile != "": + appParams := make(simulation.AppParams) + bz, err := ioutil.ReadFile(paramsFile) + if err != nil { + panic(err) + } + + cdc.MustUnmarshalJSON(bz, &appParams) + appState, simAccs, chainID = appStateRandomizedFn(r, accs, genesisTimestamp, appParams) + + default: + appParams := make(simulation.AppParams) + appState, simAccs, chainID = appStateRandomizedFn(r, accs, genesisTimestamp, appParams) + } + + return appState, simAccs, chainID, genesisTimestamp +} + +// TODO refactor out random initialization code to the modules +func appStateRandomizedFn( + r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams, +) (json.RawMessage, []simulation.Account, string) { + + cdc := MakeCodec() + genesisState := ModuleBasics.DefaultGenesis() + + var ( + amount int64 + numInitiallyBonded int64 + ) + + appParams.GetOrGenerate(cdc, StakePerAccount, &amount, r, + func(r *rand.Rand) { amount = int64(r.Intn(1e12)) }) + appParams.GetOrGenerate(cdc, InitiallyBondedValidators, &amount, r, + func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(250)) }) + + numAccs := int64(len(accs)) + if numInitiallyBonded > numAccs { + numInitiallyBonded = numAccs + } + + fmt.Printf( + `Selected randomly generated parameters for simulated genesis: +{ + stake_per_account: "%v", + initially_bonded_validators: "%v" +} +`, amount, numInitiallyBonded, + ) + + simapp.GenGenesisAccounts(cdc, r, accs, genesisTimestamp, amount, numInitiallyBonded, genesisState) + simapp.GenAuthGenesisState(cdc, r, appParams, genesisState) + simapp.GenBankGenesisState(cdc, r, appParams, genesisState) + simapp.GenSupplyGenesisState(cdc, amount, numInitiallyBonded, int64(len(accs)), genesisState) + simapp.GenGovGenesisState(cdc, r, appParams, genesisState) + simapp.GenDistrGenesisState(cdc, r, appParams, genesisState) + stakingGen := simapp.GenStakingGenesisState(cdc, r, accs, amount, numAccs, numInitiallyBonded, appParams, genesisState) + simapp.GenSlashingGenesisState(cdc, r, stakingGen, appParams, genesisState) + + appState, err := MakeCodec().MarshalJSON(genesisState) + if err != nil { + panic(err) + } + + return appState, accs, "simulation" +} + +func testAndRunTxs(app *TerraApp) []simulation.WeightedOperation { + cdc := MakeCodec() + ap := make(simulation.AppParams) + + if paramsFile != "" { + bz, err := ioutil.ReadFile(paramsFile) + if err != nil { + panic(err) + } + + cdc.MustUnmarshalJSON(bz, &ap) + } + + return []simulation.WeightedOperation{ + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightDeductFee, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + authsim.SimulateDeductFee(app.accountKeeper, app.supplyKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgSend, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + bank.SimulateMsgSend(app.accountKeeper, app.bankKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightSingleInputMsgMultiSend, &v, nil, + func(_ *rand.Rand) { + v = 10 + }) + return v + }(nil), + bank.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgSetWithdrawAddress, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgWithdrawDelegationReward, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgWithdrawValidatorCommission, &v, nil, + func(_ *rand.Rand) { + v = 50 + }) + return v + }(nil), + distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgCreateValidator, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgEditValidator, &v, nil, + func(_ *rand.Rand) { + v = 5 + }) + return v + }(nil), + stakingsim.SimulateMsgEditValidator(app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgDelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgDelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUndelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgUndelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgBeginRedelegate, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + stakingsim.SimulateMsgBeginRedelegate(app.accountKeeper, app.stakingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + slashingsim.SimulateMsgUnjail(app.slashingKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + oraclesim.SimulateMsgPrevote(app.oracleKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + oraclesim.SimulateMsgVote(app.oracleKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + oraclesim.SimulateMsgDelegateFeederPermission(app.oracleKeeper), + }, + { + func(_ *rand.Rand) int { + var v int + ap.GetOrGenerate(cdc, OpWeightMsgUnjail, &v, nil, + func(_ *rand.Rand) { + v = 100 + }) + return v + }(nil), + marketsim.SimulateMsgPrevote(app.marketKeeper), + }, + } +} + +func invariants(app *TerraApp) []sdk.Invariant { + // TODO: fix PeriodicInvariants, it doesn't seem to call individual invariants for a period of 1 + // Ref: https://github.com/cosmos/cosmos-sdk/issues/4631 + if period == 1 { + return app.crisisKeeper.Invariants() + } + return simulation.PeriodicInvariants(app.crisisKeeper.Invariants(), period, 0) +} + +// Pass this in as an option to use a dbStoreAdapter instead of an IAVLStore for simulation speed. +func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { + bapp.SetFauxMerkleMode() +} + +// Profile with: +// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/TerraApp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out +func BenchmarkFullAppSimulation(b *testing.B) { + logger := log.NewNopLogger() + + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-app-sim") + db, _ = sdk.NewLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() + app := NewTerraApp(logger, db, nil, true, 0) + + // Run randomized simulation + // TODO: parameterize numbers, save for a later PR + _, params, simErr := simulation.SimulateFromSeed(getSimulateFromSeedInput(b, os.Stdout, app)) + + // export state and params before the simulation error is checked + if exportStatePath != "" { + fmt.Println("Exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + if err != nil { + fmt.Println(err) + b.Fail() + } + err = ioutil.WriteFile(exportStatePath, []byte(appState), 0644) + if err != nil { + fmt.Println(err) + b.Fail() + } + } + + if exportParamsPath != "" { + fmt.Println("Exporting simulation params...") + paramsBz, err := json.MarshalIndent(params, "", " ") + if err != nil { + fmt.Println(err) + b.Fail() + } + + err = ioutil.WriteFile(exportParamsPath, paramsBz, 0644) + if err != nil { + fmt.Println(err) + b.Fail() + } + } + + if simErr != nil { + fmt.Println(simErr) + b.FailNow() + } + + if commit { + fmt.Println("\nGoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } +} + +func TestFullAppSimulation(t *testing.T) { + if !enabled { + t.Skip("Skipping application simulation") + } + + var logger log.Logger + + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-app-sim") + db, _ = sdk.NewLevelDB("Simulation", dir) + + defer func() { + db.Close() + os.RemoveAll(dir) + }() + + app := NewTerraApp(logger, db, nil, true, 0, fauxMerkleModeOpt) + require.Equal(t, "TerraApp", app.Name()) + + // Run randomized simulation + _, params, simErr := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) + + // export state and params before the simulation error is checked + if exportStatePath != "" { + fmt.Println("Exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + require.NoError(t, err) + + err = ioutil.WriteFile(exportStatePath, []byte(appState), 0644) + require.NoError(t, err) + } + + if exportParamsPath != "" { + fmt.Println("Exporting simulation params...") + fmt.Println(params) + paramsBz, err := json.MarshalIndent(params, "", " ") + require.NoError(t, err) + + err = ioutil.WriteFile(exportParamsPath, paramsBz, 0644) + require.NoError(t, err) + } + + require.NoError(t, simErr) + + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("\nGoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } +} + +func TestAppImportExport(t *testing.T) { + if !enabled { + t.Skip("Skipping application import/export simulation") + } + + var logger log.Logger + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-app-sim") + db, _ = sdk.NewLevelDB("Simulation", dir) + + defer func() { + db.Close() + os.RemoveAll(dir) + }() + + app := NewTerraApp(logger, db, nil, true, 0, fauxMerkleModeOpt) + require.Equal(t, "TerraApp", app.Name()) + + // Run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) + + // export state and simParams before the simulation error is checked + if exportStatePath != "" { + fmt.Println("Exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + require.NoError(t, err) + + err = ioutil.WriteFile(exportStatePath, []byte(appState), 0644) + require.NoError(t, err) + } + + if exportParamsPath != "" { + fmt.Println("Exporting simulation params...") + simParamsBz, err := json.MarshalIndent(simParams, "", " ") + require.NoError(t, err) + + err = ioutil.WriteFile(exportParamsPath, simParamsBz, 0644) + require.NoError(t, err) + } + + require.NoError(t, simErr) + + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("\nGoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } + + fmt.Printf("Exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators(false, []string{}) + require.NoError(t, err) + fmt.Printf("Importing genesis...\n") + + newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") + newDB, _ := sdk.NewLevelDB("Simulation-2", dir) + + defer func() { + newDB.Close() + os.RemoveAll(newDir) + }() + + newApp := NewTerraApp(log.NewNopLogger(), newDB, nil, true, 0, fauxMerkleModeOpt) + require.Equal(t, "TerraApp", newApp.Name()) + + var genesisState simapp.GenesisState + err = app.cdc.UnmarshalJSON(appState, &genesisState) + if err != nil { + panic(err) + } + + ctxB := newApp.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + newApp.mm.InitGenesis(ctxB, genesisState) + + fmt.Printf("Comparing stores...\n") + ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + + type StoreKeysPrefixes struct { + A sdk.StoreKey + B sdk.StoreKey + Prefixes [][]byte + } + + storeKeysPrefixes := []StoreKeysPrefixes{ + {app.keys[baseapp.MainStoreKey], newApp.keys[baseapp.MainStoreKey], [][]byte{}}, + {app.keys[auth.StoreKey], newApp.keys[auth.StoreKey], [][]byte{}}, + {app.keys[staking.StoreKey], newApp.keys[staking.StoreKey], + [][]byte{ + staking.UnbondingQueueKey, staking.RedelegationQueueKey, staking.ValidatorQueueKey, + }}, // ordering may change but it doesn't matter + {app.keys[slashing.StoreKey], newApp.keys[slashing.StoreKey], [][]byte{}}, + {app.keys[distr.StoreKey], newApp.keys[distr.StoreKey], [][]byte{}}, + {app.keys[supply.StoreKey], newApp.keys[supply.StoreKey], [][]byte{}}, + {app.keys[params.StoreKey], newApp.keys[params.StoreKey], [][]byte{}}, + {app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}}, + {app.keys[oracle.StoreKey], newApp.keys[oracle.StoreKey], [][]byte{}}, + {app.keys[treasury.StoreKey], newApp.keys[treasury.StoreKey], [][]byte{}}, + {app.keys[market.StoreKey], newApp.keys[market.StoreKey], [][]byte{}}, + } + + for _, storeKeysPrefix := range storeKeysPrefixes { + storeKeyA := storeKeysPrefix.A + storeKeyB := storeKeysPrefix.B + prefixes := storeKeysPrefix.Prefixes + storeA := ctxA.KVStore(storeKeyA) + storeB := ctxB.KVStore(storeKeyB) + kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes) + fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB) + require.True(t, equal, simapp.GetSimulationLog(storeKeyA.Name(), app.cdc, newApp.cdc, kvA, kvB)) + } + +} + +func TestAppSimulationAfterImport(t *testing.T) { + if !enabled { + t.Skip("Skipping application simulation after import") + } + + var logger log.Logger + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + + dir, _ := ioutil.TempDir("", "goleveldb-app-sim") + db, _ := sdk.NewLevelDB("Simulation", dir) + + defer func() { + db.Close() + os.RemoveAll(dir) + }() + + app := NewTerraApp(logger, db, nil, true, 0, fauxMerkleModeOpt) + require.Equal(t, "TerraApp", app.Name()) + + // Run randomized simulation + stopEarly, params, simErr := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, app)) + + // export state and params before the simulation error is checked + if exportStatePath != "" { + fmt.Println("Exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + require.NoError(t, err) + + err = ioutil.WriteFile(exportStatePath, []byte(appState), 0644) + require.NoError(t, err) + } + + if exportParamsPath != "" { + fmt.Println("Exporting simulation params...") + paramsBz, err := json.MarshalIndent(params, "", " ") + require.NoError(t, err) + + err = ioutil.WriteFile(exportParamsPath, paramsBz, 0644) + require.NoError(t, err) + } + + require.NoError(t, simErr) + + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("\nGoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } + + if stopEarly { + // we can't export or import a zero-validator genesis + fmt.Printf("We can't export or import a zero-validator genesis, exiting test...\n") + return + } + + fmt.Printf("Exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators(true, []string{}) + if err != nil { + panic(err) + } + + fmt.Printf("Importing genesis...\n") + + newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") + newDB, _ := sdk.NewLevelDB("Simulation-2", dir) + + defer func() { + newDB.Close() + os.RemoveAll(newDir) + }() + + newApp := NewTerraApp(log.NewNopLogger(), newDB, nil, true, 0, fauxMerkleModeOpt) + require.Equal(t, "TerraApp", newApp.Name()) + newApp.InitChain(abci.RequestInitChain{ + AppStateBytes: appState, + }) + + // Run randomized simulation on imported app + _, _, err = simulation.SimulateFromSeed(getSimulateFromSeedInput(t, os.Stdout, newApp)) + require.Nil(t, err) +} + +// TODO: Make another test for the fuzzer itself, which just has noOp txs +// and doesn't depend on the application. +func TestAppStateDeterminism(t *testing.T) { + if !enabled { + t.Skip("Skipping application simulation") + } + + numSeeds := 3 + numTimesToRunPerSeed := 5 + appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + + for i := 0; i < numSeeds; i++ { + seed := rand.Int63() + for j := 0; j < numTimesToRunPerSeed; j++ { + logger := log.NewNopLogger() + db := dbm.NewMemDB() + app := NewTerraApp(logger, db, nil, true, 0) + + // Run randomized simulation + simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, appStateFn, seed, testAndRunTxs(app), + []sdk.Invariant{}, 1, numBlocks, exportParamsHeight, + blockSize, "", false, commit, lean, + false, false, app.ModuleAccountAddrs(), + ) + appHash := app.LastCommitID().Hash + appHashList[j] = appHash + } + for k := 1; k < numTimesToRunPerSeed; k++ { + require.Equal(t, appHashList[0], appHashList[k], "appHash list: %v", appHashList) + } + } +} + +func BenchmarkInvariants(b *testing.B) { + logger := log.NewNopLogger() + dir, _ := ioutil.TempDir("", "goleveldb-app-invariant-bench") + db, _ := sdk.NewLevelDB("simulation", dir) + + defer func() { + db.Close() + os.RemoveAll(dir) + }() + + app := NewTerraApp(logger, db, nil, true, 0) + exportParams := exportParamsPath != "" + + // 2. Run parameterized simulation (w/o invariants) + _, params, simErr := simulation.SimulateFromSeed( + b, ioutil.Discard, app.BaseApp, appStateFn, seed, testAndRunTxs(app), + []sdk.Invariant{}, initialBlockHeight, numBlocks, exportParamsHeight, blockSize, + exportStatsPath, exportParams, commit, lean, onOperation, false, app.ModuleAccountAddrs(), + ) + + // export state and params before the simulation error is checked + if exportStatePath != "" { + fmt.Println("Exporting app state...") + appState, _, err := app.ExportAppStateAndValidators(false, nil) + if err != nil { + fmt.Println(err) + b.Fail() + } + err = ioutil.WriteFile(exportStatePath, []byte(appState), 0644) + if err != nil { + fmt.Println(err) + b.Fail() + } + } + + if exportParamsPath != "" { + fmt.Println("Exporting simulation params...") + paramsBz, err := json.MarshalIndent(params, "", " ") + if err != nil { + fmt.Println(err) + b.Fail() + } + + err = ioutil.WriteFile(exportParamsPath, paramsBz, 0644) + if err != nil { + fmt.Println(err) + b.Fail() + } + } + + if simErr != nil { + fmt.Println(simErr) + b.FailNow() + } + + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1}) + + // 3. Benchmark each invariant separately + // + // NOTE: We use the crisis keeper as it has all the invariants registered with + // their respective metadata which makes it useful for testing/benchmarking. + for _, cr := range app.crisisKeeper.Routes() { + b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) { + if res, stop := cr.Invar(ctx); stop { + fmt.Printf("broken invariant at block %d of %d\n%s", ctx.BlockHeight()-1, numBlocks, res) + b.FailNow() + } + }) + } +} diff --git a/app/utils.go b/app/utils.go new file mode 100644 index 000000000..b8fa6361e --- /dev/null +++ b/app/utils.go @@ -0,0 +1,47 @@ +//nolint +package app + +import ( + "io" + + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +var ( + genesisFile string + paramsFile string + exportParamsPath string + exportParamsHeight int + exportStatePath string + exportStatsPath string + seed int64 + initialBlockHeight int + numBlocks int + blockSize int + enabled bool + verbose bool + lean bool + commit bool + period int + onOperation bool // TODO Remove in favor of binary search for invariant violation + allInvariants bool + genesisTime int64 +) + +// DONTCOVER + +// NewTerraAppUNSAFE is used for debugging purposes only. +// +// NOTE: to not use this function with non-test code +func NewTerraAppUNSAFE(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, + invCheckPeriod uint, baseAppOptions ...func(*baseapp.BaseApp), +) (tapp *TerraApp, keyMain, keyStaking *sdk.KVStoreKey, stakingKeeper staking.Keeper) { + + tapp = NewTerraApp(logger, db, traceStore, loadLatest, invCheckPeriod, baseAppOptions...) + return tapp, tapp.keys[baseapp.MainStoreKey], tapp.keys[staking.StoreKey], tapp.stakingKeeper +} diff --git a/cli_test/README.md b/cli_test/README.md new file mode 100644 index 000000000..54c6f801e --- /dev/null +++ b/cli_test/README.md @@ -0,0 +1,51 @@ +# Terra CLI Integration tests + +The terra cli integration tests live in this folder. You can run the full suite by running: + +```bash +go test -mod=readonly -p 4 `go list ./cli_test/...` -tags=cli_test +``` + +> NOTE: While the full suite runs in parallel, some of the tests can take up to a minute to complete + +### Test Structure + +This integration suite [uses a thin wrapper](https://godoc.org/github.com/cosmos/cosmos-sdk/tests) over the [`os/exec`](https://golang.org/pkg/os/exec/) package. This allows the integration test to run against built binaries (both `terrad` and `terracli` are used) while being written in golang. This allows tests to take advantage of the various golang code we have for operations like marshal/unmarshal, crypto, etc... + +> NOTE: The tests will use whatever `terrad` or `terracli` binaries are available in your `$PATH`. You can check which binary will be run by the suite by running `which terrad` or `which terracli`. If you have your `$GOPATH` properly setup they should be in `$GOPATH/bin/terra*`. This will ensure that your test uses the latest binary you have built + +Tests generally follow this structure: + +```go +func TestMyNewCommand(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + // Your test code goes here... + + f.Cleanup() +} +``` + +This boilerplate above: + +- Ensures the tests run in parallel. Because the tests are calling out to `os/exec` for many operations these tests can take a long time to run. +- Creates `.terrad` and `.terracli` folders in a new temp folder. +- Uses `terracli` to create 2 accounts for use in testing: `foo` and `bar` +- Creates a genesis file with coins (`1000footoken,1000feetoken,150uluna`) controlled by the `foo` key +- Generates an initial bonding transaction (`gentx`) to make the `foo` key a validator at genesis +- Starts `terrad` and stops it once the test exits +- Cleans up test state on a successful run + +### Notes when adding/running tests + +- Because the tests run against a built binary, you should make sure you build every time the code changes and you want to test again, otherwise you will be testing against an older version. If you are adding new tests this can easily lead to confusing test results. +- The [`test_helpers.go`](./test_helpers.go) file is organized according to the format of `terracli` and `terrad` commands. There are comments with section headers describing the different areas. Helper functions to call CLI functionality are generally named after the command (e.g. `terracli query staking validator` would be `QueryStakingValidator`). Try to keep functions grouped by their position in the command tree. +- Test state that is needed by `tx` and `query` commands (`home`, `chain_id`, etc...) is stored on the `Fixtures` object. This makes constructing your new tests almost trivial. +- Sometimes if you exit a test early there can be still running `terrad` and `terracli` processes that will interrupt subsequent runs. Still running `terracli` processes will block access to the keybase while still running `terrad` processes will block ports and prevent new tests from spinning up. You can ensure new tests spin up clean by running `pkill -9 terrad && pkill -9 terracli` before each test run. +- Most `query` and `tx` commands take a variadic `flags` argument. This pattern allows for the creation of a general function which is easily modified by adding flags. See the `TxSend` function and its use for a good example. +- `Tx*` functions follow a general pattern and return `(success bool, stdout string, stderr string)`. This allows for easy testing of multiple different flag configurations. See `TestTerraCLICreateValidator` or `TestTerraCLISubmitProposal` for a good example of the pattern. diff --git a/cli_test/cli_test.go b/cli_test/cli_test.go new file mode 100644 index 000000000..9aa0c1e0b --- /dev/null +++ b/cli_test/cli_test.go @@ -0,0 +1,1050 @@ +// +build cli_test + +package clitest + +import ( + "encoding/base64" + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/tendermint/tendermint/crypto/ed25519" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/stretchr/testify/require" + + "github.com/terra-project/core/app" + core "github.com/terra-project/core/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + distr "github.com/terra-project/core/x/distribution" + "github.com/terra-project/core/x/genaccounts" +) + +func init() { + // Read in the configuration file for the sdk + config := sdk.GetConfig() + config.SetCoinType(core.CoinType) + config.SetFullFundraiserPath(core.FullFundraiserPath) + config.SetBech32PrefixForAccount(core.Bech32PrefixAccAddr, core.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(core.Bech32PrefixValAddr, core.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(core.Bech32PrefixConsAddr, core.Bech32PrefixConsPub) + config.Seal() +} + +func TestTerraCLIKeysAddMultisig(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // key names order does not matter + f.KeysAdd("msig1", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBar, keyBaz)) + f.KeysAdd("msig2", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBaz, keyBar)) + require.Equal(t, f.KeysShow("msig1").Address, f.KeysShow("msig2").Address) + + f.KeysAdd("msig3", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBar, keyBaz), + "--nosort") + f.KeysAdd("msig4", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBaz, keyBar), + "--nosort") + require.NotEqual(t, f.KeysShow("msig3").Address, f.KeysShow("msig4").Address) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIKeysAddRecover(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + exitSuccess, _, _ := f.KeysAddRecover("empty-mnemonic", "") + require.False(t, exitSuccess) + + exitSuccess, _, _ = f.KeysAddRecover("test-recover", "dentist task convince chimney quality leave banana trade firm crawl eternal easily") + require.True(t, exitSuccess) + require.Equal(t, "terra1pdh6ynjw382js3zm2z393jgyf3g0xa37kkxyze", f.KeyAddress("test-recover").String()) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIKeysAddRecoverHDPath(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + f.KeysAddRecoverHDPath("test-recoverHD1", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 0, 0) + require.Equal(t, "terra1pdh6ynjw382js3zm2z393jgyf3g0xa37kkxyze", f.KeyAddress("test-recoverHD1").String()) + + f.KeysAddRecoverHDPath("test-recoverH2", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 5) + require.Equal(t, "terra13chtwj64nwr6pjnk040uycu6w8e8mlh7p7sff0", f.KeyAddress("test-recoverH2").String()) + + f.KeysAddRecoverHDPath("test-recoverH3", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 17) + require.Equal(t, "terra1mqcr80y72wapapc9ekgg0x4nww4dgat6kpywnf", f.KeyAddress("test-recoverH3").String()) + + f.KeysAddRecoverHDPath("test-recoverH4", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 2, 17) + require.Equal(t, "terra1dyzv3suxyqdlsqzwj6a3rqur2qdunxcrse8tt6", f.KeyAddress("test-recoverH4").String()) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIMinimumFees(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + fees := fmt.Sprintf( + "--minimum-gas-prices=%s,%s", + sdk.NewDecCoinFromDec(feeDenom, minGasPrice), + sdk.NewDecCoinFromDec(fee2Denom, minGasPrice), + ) + proc := f.TDStart(fees) + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + + // Send a transaction that will get rejected + success, stdOut, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), "-y") + require.Contains(t, stdOut, "insufficient fees") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ correct fees pass + txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 10003)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), txFees, "-y") + require.True(f.T, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ improper fees fails + txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 1)) + success, stdOut, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees, "-y") + require.Contains(t, stdOut, "insufficient fees") + require.True(f.T, success) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIGasPrices(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + proc := f.TDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice))) + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + + // insufficient gas prices (tx fails) + badGasPrice, _ := sdk.NewDecFromStr("0.000003") + success, stdOut, _ := f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50), + fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, badGasPrice)), "-y") + require.Contains(t, stdOut, "insufficient fees") + require.True(t, success) + + // wait for a block confirmation + tests.WaitForNextNBlocksTM(1, f.Port) + + // sufficient gas prices (tx passes) + success, _, _ = f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50), + fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)), "-y") + require.True(t, success) + + // wait for a block confirmation + tests.WaitForNextNBlocksTM(1, f.Port) + + f.Cleanup() +} + +func TestTerraCLIFeesDeduction(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + proc := f.TDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice))) + defer proc.Stop(false) + + // Save key addresses for later use + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + fooAmt := fooAcc.GetCoins().AmountOf(fooDenom) + + // test simulation + success, _, _ := f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 1000), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 10003)), "--dry-run") + require.True(t, success) + + // Wait for a block + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, fooAmt.Int64(), fooAcc.GetCoins().AmountOf(fooDenom).Int64()) + + // insufficient funds (coins + fees) tx fails + largeCoins := sdk.TokensFromConsensusPower(10000000) + success, stdOut, _ := f.TxSend( + keyFoo, barAddr, sdk.NewCoin(fooDenom, largeCoins), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 1000000)), "-y") + require.Contains(t, stdOut, "insufficient fees") + require.True(t, success) + + // Wait for a block + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, fooAmt.Int64(), fooAcc.GetCoins().AmountOf(fooDenom).Int64()) + + // test success (transfer = coins + fees) + success, _, _ = f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 500), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 10002)), "-y") + require.True(t, success) + + f.Cleanup() +} + +func TestTerraCLISend(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + // Save key addresses for later use + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromConsensusPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Send some tokens from one account to the other + sendTokens := sdk.TokensFromConsensusPower(10) + fee := sdk.NewInt64Coin(denom, 10001) + txFees := fmt.Sprintf("--fees=%s", fee) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y", txFees) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc := f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens).Sub(fee.Amount), fooAcc.GetCoins().AmountOf(denom)) + + // Test --dry-run + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--dry-run") + require.True(t, success) + + // Test --generate-only + success, stdOut, stderr := f.TxSend( + fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only=true", + ) + require.Empty(t, stderr) + require.True(t, success) + msg := unmarshalStdTx(f.T, stdOut) + require.NotZero(t, msg.Fee.Gas) + require.Len(t, msg.Msgs, 1) + require.Len(t, msg.GetSignatures(), 0) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens).Sub(fee.Amount), fooAcc.GetCoins().AmountOf(denom)) + + // test autosequencing + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y", txFees) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.MulRaw(2), barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(2)).Sub(fee.Amount.MulRaw(2)), fooAcc.GetCoins().AmountOf(denom)) + + // test memo + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--memo='testmemo'", "-y", txFees) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.MulRaw(3), barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(3)).Sub(fee.Amount.MulRaw(3)), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestTerraCLIGasAuto(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromConsensusPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + fee := sdk.NewInt64Coin(denom, 10005) + txFees := fmt.Sprintf("--fees=%s", fee) + + // Test failure with auto gas disabled and very little gas set by hand + sendTokens := sdk.TokensFromConsensusPower(10) + success, stdOut, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=10", "-y", txFees) + require.Contains(t, stdOut, "out of gas in location") + require.True(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test failure with negative gas + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=-100", "-y", txFees) + require.False(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test failure with 0 gas + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=0", "-y", txFees) + require.Contains(t, stdOut, "out of gas in location") + require.True(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Enable auto gas + success, stdOut, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "-y", txFees) + require.NotEmpty(t, stderr) + require.True(t, success) + cdc := app.MakeCodec() + sendResp := sdk.TxResponse{} + err := cdc.UnmarshalJSON([]byte(stdOut), &sendResp) + require.Nil(t, err) + require.True(t, sendResp.GasWanted >= sendResp.GasUsed) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Check state has changed accordingly + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens).Sub(fee.Amount), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestTerraCLICreateValidator(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + barVal := sdk.ValAddress(barAddr) + + consPubKey := sdk.MustBech32ifyConsPub(ed25519.GenPrivKey().PubKey()) + + sendTokens := sdk.TokensFromConsensusPower(10) + txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(denom, 10001)) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y", txFees) + tests.WaitForNextNBlocksTM(1, f.Port) + + barAcc := f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + + // Generate a create validator transaction and ensure correctness + success, stdOut, stderr := f.TxStakingCreateValidator(barAddr.String(), consPubKey, sdk.NewInt64Coin(denom, 2), "--generate-only") + + require.True(f.T, success) + require.Empty(f.T, stderr) + msg := unmarshalStdTx(f.T, stdOut) + require.NotZero(t, msg.Fee.Gas) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test --dry-run + newValTokens := sdk.TokensFromConsensusPower(2) + success, _, _ = f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "--dry-run") + require.True(t, success) + + // Create the validator + f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure funds were deducted properly + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.Sub(newValTokens), barAcc.GetCoins().AmountOf(denom)) + + // Ensure that validator state is as expected + validator := f.QueryStakingValidator(barVal) + require.Equal(t, validator.OperatorAddress, barVal) + require.True(sdk.IntEq(t, newValTokens, validator.Tokens)) + + // Query delegations to the validator + validatorDelegations := f.QueryStakingDelegationsTo(barVal) + require.Len(t, validatorDelegations, 1) + require.NotZero(t, validatorDelegations[0].Shares) + + // unbond a single share + unbondAmt := sdk.NewCoin(core.MicroLunaDenom, sdk.TokensFromConsensusPower(1)) + success = f.TxStakingUnbond(keyBar, unbondAmt.String(), barVal, "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure bonded staking is correct + remainingTokens := newValTokens.Sub(unbondAmt.Amount) + validator = f.QueryStakingValidator(barVal) + require.Equal(t, remainingTokens, validator.Tokens) + + // Get unbonding delegations from the validator + validatorUbds := f.QueryStakingUnbondingDelegationsFrom(barVal) + require.Len(t, validatorUbds, 1) + require.Len(t, validatorUbds[0].Entries, 1) + require.Equal(t, remainingTokens.String(), validatorUbds[0].Entries[0].Balance.String()) + + f.Cleanup() +} + +func TestTerraCLIQueryRewards(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + cdc := app.MakeCodec() + + genesisState := f.GenesisState() + var distrData distr.GenesisState + cdc.UnmarshalJSON(genesisState[distr.ModuleName], &distrData) + distrData.FeePool.CommunityPool = sdk.DecCoins{sdk.NewDecCoinFromDec(core.MicroLunaDenom, sdk.NewDec(1))} + distrDataBz, err := cdc.MarshalJSON(distrData) + require.NoError(t, err) + genesisState[distr.ModuleName] = distrDataBz + + genFile := filepath.Join(f.TerradHome, "config", "genesis.json") + genDoc, err := tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + genDoc.AppState, err = cdc.MarshalJSON(genesisState) + require.NoError(t, genDoc.SaveAs(genFile)) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + rewards := f.QueryRewards(fooAddr) + require.Equal(t, 1, len(rewards.Rewards)) + + f.Cleanup() +} + +func TestTerraCLIQuerySupply(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + totalSupply := f.QueryTotalSupply() + totalSupplyOf := f.QueryTotalSupplyOf(fooDenom) + + require.Equal(t, totalCoins, totalSupply) + require.True(sdk.IntEq(t, totalCoins.AmountOf(fooDenom), totalSupplyOf)) + + f.Cleanup() +} + +func TestTerraCLIQueryTxPagination(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + accFoo := f.QueryAccount(fooAddr) + seq := accFoo.GetSequence() + + for i := 1; i <= 30; i++ { + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, int64(i)), fmt.Sprintf("--sequence=%d", seq), "-y") + require.True(t, success) + seq++ + } + + // perPage = 15, 2 pages + txsPage1 := f.QueryTxs(1, 15, fmt.Sprintf("message.sender:%s", fooAddr)) + require.Len(t, txsPage1.Txs, 15) + require.Equal(t, txsPage1.Count, 15) + txsPage2 := f.QueryTxs(2, 15, fmt.Sprintf("message.sender:%s", fooAddr)) + require.Len(t, txsPage2.Txs, 15) + require.NotEqual(t, txsPage1.Txs, txsPage2.Txs) + + // perPage = 16, 2 pages + txsPage1 = f.QueryTxs(1, 16, fmt.Sprintf("message.sender:%s", fooAddr)) + require.Len(t, txsPage1.Txs, 16) + txsPage2 = f.QueryTxs(2, 16, fmt.Sprintf("message.sender:%s", fooAddr)) + require.Len(t, txsPage2.Txs, 14) + require.NotEqual(t, txsPage1.Txs, txsPage2.Txs) + + // perPage = 50 + txsPageFull := f.QueryTxs(1, 50, fmt.Sprintf("message.sender:%s", fooAddr)) + require.Len(t, txsPageFull.Txs, 30) + require.Equal(t, txsPageFull.Txs, append(txsPage1.Txs, txsPage2.Txs...)) + + // perPage = 0 + f.QueryTxsInvalid(errors.New("ERROR: page must greater than 0"), 0, 50, fmt.Sprintf("message.sender:%s", fooAddr)) + + // limit = 0 + f.QueryTxsInvalid(errors.New("ERROR: limit must greater than 0"), 1, 0, fmt.Sprintf("message.sender:%s", fooAddr)) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIValidateSignatures(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + // generate sendTx with default gas + success, stdOut, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + + // write unsigned tx to file + unsignedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(unsignedTxFile.Name()) + + // validate we can successfully sign + success, stdOut, stderr = f.TxSign(keyFoo, unsignedTxFile.Name()) + require.True(t, success) + require.Empty(t, stderr) + stdTx := unmarshalStdTx(t, stdOut) + require.Equal(t, len(stdTx.Msgs), 1) + require.Equal(t, 1, len(stdTx.GetSignatures())) + require.Equal(t, fooAddr.String(), stdTx.GetSigners()[0].String()) + + // write signed tx to file + signedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(signedTxFile.Name()) + + // validate signatures + success, _, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + + // modify the transaction + stdTx.Memo = "MODIFIED-ORIGINAL-TX-BAD" + bz := marshalStdTx(t, stdTx) + modSignedTxFile := WriteToNewTempFile(t, string(bz)) + defer os.Remove(modSignedTxFile.Name()) + + // validate signature validation failure due to different transaction sig bytes + success, _, _ = f.TxSign(keyFoo, modSignedTxFile.Name(), "--validate-signatures") + require.False(t, success) + + f.Cleanup() +} + +func TestTerraCLISendGenerateSignAndBroadcast(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + // Test generate sendTx with default gas + sendTokens := sdk.TokensFromConsensusPower(10) + success, stdOut, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg := unmarshalStdTx(t, stdOut) + require.Equal(t, msg.Fee.Gas, uint64(client.DefaultGasLimit)) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test generate sendTx with --gas=$amount + success, stdOut, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=100", "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg = unmarshalStdTx(t, stdOut) + require.Equal(t, msg.Fee.Gas, uint64(100)) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test generate sendTx, estimate gas + fee := sdk.NewInt64Coin(denom, 10001) + txFees := fmt.Sprintf("--fees=%s", fee) + success, stdOut, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), txFees, "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg = unmarshalStdTx(t, stdOut) + require.True(t, msg.Fee.Gas > 0) + require.Equal(t, len(msg.Msgs), 1) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(unsignedTxFile.Name()) + + // TODO - check estimate fee + // success, stdOut, stderr = f.TxEstimateFee(unsignedTxFile.Name(), "--gas-adjustment=1.4", "--gas-prices=0.015uluna") + // require.True(t, success) + // require.Empty(t, stderr) + // estimateResult := unmarshalEstimateFeeResult(t, stdOut) + + // Test sign --validate-signatures + success, stdOut, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--validate-signatures") + require.False(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdOut) + + // Test sign + success, stdOut, _ = f.TxSign(keyFoo, unsignedTxFile.Name()) + require.True(t, success) + msg = unmarshalStdTx(t, stdOut) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 1, len(msg.GetSignatures())) + require.Equal(t, fooAddr.String(), msg.GetSigners()[0].String()) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(signedTxFile.Name()) + + // Test sign --validate-signatures + success, stdOut, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t\t\t[OK]\n\n", fooAddr.String(), + fooAddr.String()), stdOut) + + // Ensure foo has right amount of funds + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromConsensusPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test broadcast + success, stdOut, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account state + barAcc := f.QueryAccount(barAddr) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + require.Equal(t, startTokens.Sub(sendTokens).Sub(fee.Amount), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestTerraCLIMultisignInsufficientCosigners(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + proc := f.TDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + barAddr := f.KeyAddress(keyBar) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Test generate sendTx with multisig + success, stdOut, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + require.True(t, success) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdOut, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(fooSignatureFile.Name()) + + // Multisign, not enough signatures + success, stdOut, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{fooSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures") + require.False(t, success) + + // Broadcast the transaction + success, stdOut, _ = f.TxBroadcast(signedTxFile.Name()) + require.Contains(t, stdOut, "signature verification failed") + require.True(t, success) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIEncode(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + cdc := app.MakeCodec() + + // Build a testing transaction and write it to disk + barAddr := f.KeyAddress(keyBar) + keyAddr := f.KeyAddress(keyFoo) + + sendTokens := sdk.TokensFromConsensusPower(10) + success, stdOut, stderr := f.TxSend(keyAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only", "--memo", "deadbeef") + require.True(t, success) + require.Empty(t, stderr) + + // Write it to disk + jsonTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(jsonTxFile.Name()) + + // Run the encode command, and trim the extras from the stdOut capture + success, base64Encoded, _ := f.TxEncode(jsonTxFile.Name()) + require.True(t, success) + trimmedBase64 := strings.Trim(base64Encoded, "\"\n") + + // Decode the base64 + decodedBytes, err := base64.StdEncoding.DecodeString(trimmedBase64) + require.Nil(t, err) + + // Check that the transaction decodes as epxceted + var decodedTx authtypes.StdTx + require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx)) + require.Equal(t, "deadbeef", decodedTx.Memo) +} + +func TestTerraCLIMultisignSortSignatures(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + proc := f.TDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + barAddr := f.KeyAddress(keyBar) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + fooBarBazAcc := f.QueryAccount(fooBarBazAddr) + require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) + + // Test generate sendTx with multisig + success, stdOut, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + require.True(t, success) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdOut, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String()) + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(fooSignatureFile.Name()) + + // Sign with baz's key + success, stdOut, _ = f.TxSign(keyBaz, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String()) + require.True(t, success) + + // Write the output to disk + bazSignatureFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(bazSignatureFile.Name()) + + // Multisign, keys in different order + success, stdOut, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{ + bazSignatureFile.Name(), fooSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + + // Broadcast the transaction + success, _, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIMultisign(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server with minimum fees + proc := f.TDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + bazAddr := f.KeyAddress(keyBaz) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + fooBarBazAcc := f.QueryAccount(fooBarBazAddr) + require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) + + // Test generate sendTx with multisig + success, stdOut, stderr := f.TxSend(fooBarBazAddr.String(), bazAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdOut, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(fooSignatureFile.Name()) + + // Sign with bar's key + success, stdOut, _ = f.TxSign(keyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + barSignatureFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(barSignatureFile.Name()) + + // Multisign + success, stdOut, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{ + fooSignatureFile.Name(), barSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdOut) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "-y") + require.True(t, success) + + // Broadcast the transaction + success, _, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) + + // Cleanup testing directories + f.Cleanup() +} + +func TestTerraCLIConfig(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + node := fmt.Sprintf("%s:%s", f.RPCAddr, f.Port) + + // Set available configuration options + f.CLIConfig("broadcast-mode", "block") + f.CLIConfig("node", node) + f.CLIConfig("output", "text") + f.CLIConfig("trust-node", "true") + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("trace", "false") + f.CLIConfig("indent", "true") + + config, err := ioutil.ReadFile(path.Join(f.TerracliHome, "config", "config.toml")) + require.NoError(t, err) + expectedConfig := fmt.Sprintf(`broadcast-mode = "block" +chain-id = "%s" +indent = true +node = "%s" +output = "text" +trace = false +trust-node = true +`, f.ChainID, node) + require.Equal(t, expectedConfig, string(config)) + + f.Cleanup() +} + +func TestTerradCollectGentxs(t *testing.T) { + t.Parallel() + var customMaxBytes, customMaxGas int64 = 99999999, 1234567 + f := NewFixtures(t) + + // Initialise temporary directories + gentxDir, err := ioutil.TempDir("", "") + gentxDoc := filepath.Join(gentxDir, "gentx.json") + require.NoError(t, err) + + // Reset testing path + f.UnsafeResetAll() + + // Initialize keys + f.KeysAdd(keyFoo) + + // Configure json output + f.CLIConfig("output", "json") + + // Run init + f.TDInit(keyFoo) + + // Customise genesis.json + + genFile := f.GenesisFile() + genDoc, err := tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + genDoc.ConsensusParams.Block.MaxBytes = customMaxBytes + genDoc.ConsensusParams.Block.MaxGas = customMaxGas + genDoc.SaveAs(genFile) + + // Add account to genesis.json + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + + // Write gentx file + f.GenTx(keyFoo, fmt.Sprintf("--output-document=%s", gentxDoc)) + + // Collect gentxs from a custom directory + f.CollectGenTxs(fmt.Sprintf("--gentx-dir=%s", gentxDir)) + + genDoc, err = tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + require.Equal(t, genDoc.ConsensusParams.Block.MaxBytes, customMaxBytes) + require.Equal(t, genDoc.ConsensusParams.Block.MaxGas, customMaxGas) + + f.Cleanup(gentxDir) +} + +func TestTerradAddGenesisAccount(t *testing.T) { + t.Parallel() + f := NewFixtures(t) + + // Reset testing path + f.UnsafeResetAll() + + // Initialize keys + f.KeysDelete(keyFoo) + f.KeysDelete(keyBar) + f.KeysDelete(keyBaz) + f.KeysAdd(keyFoo) + f.KeysAdd(keyBar) + f.KeysAdd(keyBaz) + + // Configure json output + f.CLIConfig("output", "json") + + // Run init + f.TDInit(keyFoo) + + // Add account to genesis.json + bazCoins := sdk.Coins{ + sdk.NewInt64Coin("acoin", 1000000), + sdk.NewInt64Coin("bcoin", 1000000), + } + + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + f.AddGenesisAccount(f.KeyAddress(keyBar), bazCoins) + genesisState := f.GenesisState() + + cdc := app.MakeCodec() + accounts := genaccounts.GetGenesisStateFromAppState(cdc, genesisState) + + require.Equal(t, accounts[0].Address, f.KeyAddress(keyFoo)) + require.Equal(t, accounts[1].Address, f.KeyAddress(keyBar)) + require.True(t, accounts[0].Coins.IsEqual(startCoins)) + require.True(t, accounts[1].Coins.IsEqual(bazCoins)) + + // Cleanup testing directories + f.Cleanup() +} + +func TestSlashingGetParams(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + params := f.QuerySlashingParams() + require.Equal(t, time.Duration(120000000000), params.MaxEvidenceAge) + require.Equal(t, int64(100), params.SignedBlocksWindow) + require.Equal(t, sdk.NewDecWithPrec(5, 1), params.MinSignedPerWindow) + + sinfo := f.QuerySigningInfo(f.TDTendermint("show-validator")) + require.Equal(t, int64(0), sinfo.StartHeight) + require.False(t, sinfo.Tombstoned) + + // Cleanup testing directories + f.Cleanup() +} + +func TestValidateGenesis(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start terrad server + proc := f.TDStart() + defer proc.Stop(false) + + f.ValidateGenesis() + + // Cleanup testing directories + f.Cleanup() +} diff --git a/cli_test/doc.go b/cli_test/doc.go new file mode 100644 index 000000000..bcf9c5e4d --- /dev/null +++ b/cli_test/doc.go @@ -0,0 +1,3 @@ +package clitest + +// package clitest runs integration tests which make use of CLI commands. diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go new file mode 100644 index 000000000..0139b4f9c --- /dev/null +++ b/cli_test/test_helpers.go @@ -0,0 +1,786 @@ +// Package clitest test_helper +// provides terrad&terracli binary execution for simulation +package clitest + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/stretchr/testify/require" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/terra-project/core/app" + + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + authutils "github.com/terra-project/core/x/auth/client/utils" +) + +const ( + denom = "uluna" + keyFoo = "foo" + keyBar = "bar" + fooDenom = "footoken" + feeDenom = "feetoken" + fee2Denom = "fee2token" + keyBaz = "baz" + keyVesting = "vesting" + keyFooBarBaz = "foobarbaz" +) + +var ( + totalCoins = sdk.NewCoins( + sdk.NewCoin(fee2Denom, sdk.TokensFromConsensusPower(2000000)), + sdk.NewCoin(feeDenom, sdk.TokensFromConsensusPower(2000000)), + sdk.NewCoin(fooDenom, sdk.TokensFromConsensusPower(2000)), + sdk.NewCoin(denom, sdk.TokensFromConsensusPower(300)), + ) + + startCoins = sdk.NewCoins( + sdk.NewCoin(fee2Denom, sdk.TokensFromConsensusPower(1000000)), + sdk.NewCoin(feeDenom, sdk.TokensFromConsensusPower(1000000)), + sdk.NewCoin(fooDenom, sdk.TokensFromConsensusPower(1000)), + sdk.NewCoin(denom, sdk.TokensFromConsensusPower(150)), + ) + + vestingCoins = sdk.NewCoins( + sdk.NewCoin(feeDenom, sdk.TokensFromConsensusPower(500000)), + ) +) + +//___________________________________________________________________________________ +// Fixtures + +// Fixtures is used to setup the testing environment +type Fixtures struct { + BuildDir string + RootDir string + TerradBinary string + TerracliBinary string + ChainID string + RPCAddr string + Port string + TerradHome string + TerracliHome string + P2PAddr string + T *testing.T +} + +// NewFixtures creates a new instance of Fixtures with many vars set +func NewFixtures(t *testing.T) *Fixtures { + tmpDir, err := ioutil.TempDir("", "terra_integration_"+t.Name()+"_") + require.NoError(t, err) + + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + + p2pAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + + buildDir := os.Getenv("BUILDDIR") + if buildDir == "" { + buildDir, err = filepath.Abs("../build/") + require.NoError(t, err) + } + + return &Fixtures{ + T: t, + BuildDir: buildDir, + RootDir: tmpDir, + TerradBinary: filepath.Join(buildDir, "terrad"), + TerracliBinary: filepath.Join(buildDir, "terracli"), + TerradHome: filepath.Join(tmpDir, ".terrad"), + TerracliHome: filepath.Join(tmpDir, ".terracli"), + RPCAddr: servAddr, + P2PAddr: p2pAddr, + Port: port, + } +} + +// GenesisFile returns the path of the genesis file +func (f Fixtures) GenesisFile() string { + return filepath.Join(f.TerradHome, "config", "genesis.json") +} + +// GenesisFile returns the application's genesis state +func (f Fixtures) GenesisState() simapp.GenesisState { + cdc := codec.New() + genDoc, err := tmtypes.GenesisDocFromFile(f.GenesisFile()) + require.NoError(f.T, err) + + var appState simapp.GenesisState + require.NoError(f.T, cdc.UnmarshalJSON(genDoc.AppState, &appState)) + return appState +} + +// InitFixtures is called at the beginning of a test and initializes a chain +// with 1 validator. +func InitFixtures(t *testing.T) (f *Fixtures) { + f = NewFixtures(t) + + // reset test state + f.UnsafeResetAll() + + // ensure keystore has foo and bar keys + f.KeysDelete(keyFoo) + f.KeysDelete(keyBar) + f.KeysDelete(keyBar) + f.KeysDelete(keyFooBarBaz) + f.KeysAdd(keyFoo) + f.KeysAdd(keyBar) + f.KeysAdd(keyBaz) + f.KeysAdd(keyVesting) + f.KeysAdd(keyFooBarBaz, "--multisig-threshold=2", fmt.Sprintf( + "--multisig=%s,%s,%s", keyFoo, keyBar, keyBaz)) + + // ensure that CLI output is in JSON format + f.CLIConfig("output", "json") + + // NOTE: TDInit sets the ChainID + f.TDInit(keyFoo) + + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("broadcast-mode", "block") + f.CLIConfig("trust-node", "true") + + // start an account with tokens + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + f.AddGenesisAccount( + f.KeyAddress(keyVesting), startCoins, + fmt.Sprintf("--vesting-amount=%s", vestingCoins), + fmt.Sprintf("--vesting-start-time=%d", time.Now().UTC().UnixNano()), + fmt.Sprintf("--vesting-end-time=%d", time.Now().Add(60*time.Second).UTC().UnixNano()), + ) + + f.GenTx(keyFoo) + f.CollectGenTxs() + + return +} + +// Cleanup is meant to be run at the end of a test to clean up an remaining test state +func (f *Fixtures) Cleanup(dirs ...string) { + clean := append(dirs, f.RootDir) + for _, d := range clean { + require.NoError(f.T, os.RemoveAll(d)) + } +} + +// Flags returns the flags necessary for making most CLI calls +func (f *Fixtures) Flags() string { + return fmt.Sprintf("--home=%s --node=%s", f.TerracliHome, f.RPCAddr) +} + +//___________________________________________________________________________________ +// terrad + +// UnsafeResetAll is terrad unsafe-reset-all +func (f *Fixtures) UnsafeResetAll(flags ...string) { + cmd := fmt.Sprintf("%s --home=%s unsafe-reset-all", f.TerradBinary, f.TerradHome) + executeWrite(f.T, addFlags(cmd, flags)) + err := os.RemoveAll(filepath.Join(f.TerradHome, "config", "gentx")) + require.NoError(f.T, err) +} + +// TDInit is terrad init +// NOTE: TDInit sets the ChainID for the Fixtures instance +func (f *Fixtures) TDInit(moniker string, flags ...string) { + cmd := fmt.Sprintf("%s init -o --home=%s %s", f.TerradBinary, f.TerradHome, moniker) + _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + + var chainID string + var initRes map[string]json.RawMessage + + err := json.Unmarshal([]byte(stderr), &initRes) + require.NoError(f.T, err) + + err = json.Unmarshal(initRes["chain_id"], &chainID) + require.NoError(f.T, err) + + f.ChainID = chainID +} + +// AddGenesisAccount is terrad add-genesis-account +func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) { + cmd := fmt.Sprintf("%s add-genesis-account %s %s --home=%s", f.TerradBinary, address, coins, f.TerradHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +// GenTx is terrad gentx +func (f *Fixtures) GenTx(name string, flags ...string) { + cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s --amount 100000000uluna", f.TerradBinary, name, f.TerradHome, f.TerracliHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// CollectGenTxs is terrad collect-gentxs +func (f *Fixtures) CollectGenTxs(flags ...string) { + cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.TerradBinary, f.TerradHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TDStart runs terrad start with the appropriate flags and returns a process +func (f *Fixtures) TDStart(flags ...string) *tests.Process { + cmd := fmt.Sprintf("%s start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.TerradBinary, f.TerradHome, f.RPCAddr, f.P2PAddr) + proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) + tests.WaitForTMStart(f.Port) + tests.WaitForNextNBlocksTM(1, f.Port) + return proc +} + +// TDTendermint returns the results of terrad tendermint [query] +func (f *Fixtures) TDTendermint(query string) string { + cmd := fmt.Sprintf("%s tendermint %s --home=%s", f.TerradBinary, query, f.TerradHome) + success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) + require.Empty(f.T, stderr) + require.True(f.T, success) + return strings.TrimSpace(stdout) +} + +// ValidateGenesis runs terrad validate-genesis +func (f *Fixtures) ValidateGenesis() { + cmd := fmt.Sprintf("%s validate-genesis --home=%s", f.TerradBinary, f.TerradHome) + executeWriteCheckErr(f.T, cmd) +} + +//___________________________________________________________________________________ +// terracli keys + +// KeysDelete is terracli keys delete +func (f *Fixtures) KeysDelete(name string, flags ...string) { + cmd := fmt.Sprintf("%s keys delete --home=%s %s", f.TerracliBinary, f.TerracliHome, name) + executeWrite(f.T, addFlags(cmd, append(append(flags, "-y"), "-f"))) +} + +// KeysAdd is terracli keys add +func (f *Fixtures) KeysAdd(name string, flags ...string) { + cmd := fmt.Sprintf("%s keys add --home=%s %s", f.TerracliBinary, f.TerracliHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// KeysAddRecover prepares terracli keys add --recover +func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) { + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s", f.TerracliBinary, f.TerracliHome, name) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic) +} + +// KeysAddRecoverHDPath prepares terracli keys add --recover --account --index +func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s --account %d --index %d", f.TerracliBinary, f.TerracliHome, name, account, index) + executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic) +} + +// KeysShow is terracli keys show +func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { + cmd := fmt.Sprintf("%s keys show --home=%s %s", f.TerracliBinary, f.TerracliHome, name) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var ko keys.KeyOutput + err := clientkeys.UnmarshalJSON([]byte(out), &ko) + require.NoError(f.T, err) + return ko +} + +// KeyAddress returns the SDK account address from the key +func (f *Fixtures) KeyAddress(name string) sdk.AccAddress { + ko := f.KeysShow(name) + accAddr, err := sdk.AccAddressFromBech32(ko.Address) + require.NoError(f.T, err) + return accAddr +} + +//___________________________________________________________________________________ +// terracli config + +// CLIConfig is terracli config +func (f *Fixtures) CLIConfig(key, value string, flags ...string) { + cmd := fmt.Sprintf("%s config --home=%s %s %s", f.TerracliBinary, f.TerracliHome, key, value) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +//___________________________________________________________________________________ +// terracli tx send/sign/broadcast + +// TxSend is terracli tx send +func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx send %s %s %s %v", f.TerracliBinary, from, to, amount, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxSign is terracli tx sign +func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx sign %v --from=%s %v", f.TerracliBinary, f.Flags(), signer, fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxBroadcast is terracli tx broadcast +func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx broadcast %v %v", f.TerracliBinary, f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxEncode is terracli tx encode +func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx encode %v %v", f.TerracliBinary, f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxMultisign is terracli tx multisign +func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, + flags ...string) (bool, string, string) { + + cmd := fmt.Sprintf("%s tx multisign %v %s %s %s", f.TerracliBinary, f.Flags(), + fileName, name, strings.Join(signaturesFiles, " "), + ) + return executeWriteRetStdStreams(f.T, cmd) +} + +// TxEstimateFee +func (f *Fixtures) TxEstimateFee(fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx estimate-fee %v %v", f.TerracliBinary, f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags)) +} + +//___________________________________________________________________________________ +// terracli tx staking + +// TxStakingCreateValidator is terracli tx staking create-validator +func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx staking create-validator %v --from=%s --pubkey=%s", f.TerracliBinary, f.Flags(), from, consPubKey) + cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05") + cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10") + cmd += fmt.Sprintf(" --min-self-delegation=%v", "1") + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxStakingUnbond is terracli tx staking unbond +func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { + cmd := fmt.Sprintf("%s tx staking unbond %s %v --from=%s %v", f.TerracliBinary, validator, shares, from, f.Flags()) + return executeWrite(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +//___________________________________________________________________________________ +// terracli tx gov + +// TxGovSubmitProposal is terracli tx gov submit-proposal +func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx gov submit-proposal %v --from=%s --type=%s", f.TerracliBinary, f.Flags(), from, typ) + cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxGovDeposit is terracli tx gov deposit +func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx gov deposit %d %s --from=%s %v", f.TerracliBinary, proposalID, amount, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxGovVote is terracli tx gov vote +func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx gov vote %d %s --from=%s %v", f.TerracliBinary, proposalID, option, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxGovSubmitParamChangeProposal executes a CLI parameter change proposal +// submission. +func (f *Fixtures) TxGovSubmitParamChangeProposal( + from, proposalPath string, deposit sdk.Coin, flags ...string, +) (bool, string, string) { + + cmd := fmt.Sprintf( + "%s tx gov submit-proposal param-change %s --from=%s %v", + f.TerracliBinary, proposalPath, from, f.Flags(), + ) + + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +// TxGovSubmitCommunityPoolSpendProposal executes a CLI community pool spend proposal +// submission. +func (f *Fixtures) TxGovSubmitCommunityPoolSpendProposal( + from, proposalPath string, deposit sdk.Coin, flags ...string, +) (bool, string, string) { + + cmd := fmt.Sprintf( + "%s tx gov submit-proposal community-pool-spend %s --from=%s %v", + f.TerracliBinary, proposalPath, from, f.Flags(), + ) + + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + +//___________________________________________________________________________________ +// terracli query account + +// QueryAccount is terracli query account +func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) authtypes.BaseAccount { + cmd := fmt.Sprintf("%s query account %s %v", f.TerracliBinary, address, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var initRes map[string]json.RawMessage + err := json.Unmarshal([]byte(out), &initRes) + require.NoError(f.T, err, "out %v, err %v", out, err) + value := initRes["value"] + var acc authtypes.BaseAccount + cdc := codec.New() + codec.RegisterCrypto(cdc) + err = cdc.UnmarshalJSON(value, &acc) + require.NoError(f.T, err, "value %v, err %v", string(value), err) + return acc +} + +//___________________________________________________________________________________ +// terracli query txs + +// QueryTxs is terracli query txs +func (f *Fixtures) QueryTxs(page, limit int, tags ...string) *sdk.SearchTxsResult { + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.TerracliBinary, page, limit, queryTags(tags), f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var result sdk.SearchTxsResult + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &result) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return &result +} + +// QueryTxsInvalid query txs with wrong parameters and compare expected error +func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...string) { + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.TerracliBinary, page, limit, queryTags(tags), f.Flags()) + _, err := tests.ExecuteT(f.T, cmd, "") + require.EqualError(f.T, expectedErr, err) +} + +//___________________________________________________________________________________ +// terracli query staking + +// QueryStakingValidator is terracli query staking validator +func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string) staking.Validator { + cmd := fmt.Sprintf("%s query staking validator %s %v", f.TerracliBinary, valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var validator staking.Validator + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &validator) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return validator +} + +// QueryStakingUnbondingDelegationsFrom is terracli query staking unbonding-delegations-from +func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []staking.UnbondingDelegation { + cmd := fmt.Sprintf("%s query staking unbonding-delegations-from %s %v", f.TerracliBinary, valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var ubds []staking.UnbondingDelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &ubds) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return ubds +} + +// QueryStakingDelegationsTo is terracli query staking delegations-to +func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...string) []staking.Delegation { + cmd := fmt.Sprintf("%s query staking delegations-to %s %v", f.TerracliBinary, valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var delegations []staking.Delegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &delegations) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return delegations +} + +// QueryStakingPool is terracli query staking pool +func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { + cmd := fmt.Sprintf("%s query staking pool %v", f.TerracliBinary, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var pool staking.Pool + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &pool) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return pool +} + +// QueryStakingParameters is terracli query staking parameters +func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { + cmd := fmt.Sprintf("%s query staking params %v", f.TerracliBinary, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var params staking.Params + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), ¶ms) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return params +} + +//___________________________________________________________________________________ +// terracli query gov + +// QueryGovParamDeposit is terracli query gov param deposit +func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { + cmd := fmt.Sprintf("%s query gov param deposit %s", f.TerracliBinary, f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var depositParam gov.DepositParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &depositParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return depositParam +} + +// QueryGovParamVoting is terracli query gov param voting +func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { + cmd := fmt.Sprintf("%s query gov param voting %s", f.TerracliBinary, f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var votingParam gov.VotingParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &votingParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return votingParam +} + +// QueryGovParamTallying is terracli query gov param tallying +func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { + cmd := fmt.Sprintf("%s query gov param tallying %s", f.TerracliBinary, f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var tallyingParam gov.TallyParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &tallyingParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return tallyingParam +} + +// QueryGovProposals is terracli query gov proposals +func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { + cmd := fmt.Sprintf("%s query gov proposals %v", f.TerracliBinary, f.Flags()) + stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + if strings.Contains(stderr, "No matching proposals found") { + return gov.Proposals{} + } + require.Empty(f.T, stderr) + var out gov.Proposals + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(stdout), &out) + require.NoError(f.T, err) + return out +} + +// QueryGovProposal is terracli query gov proposal +func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal { + cmd := fmt.Sprintf("%s query gov proposal %d %v", f.TerracliBinary, proposalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var proposal gov.Proposal + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &proposal) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return proposal +} + +// QueryGovVote is terracli query gov vote +func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote { + cmd := fmt.Sprintf("%s query gov vote %d %s %v", f.TerracliBinary, proposalID, voter, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var vote gov.Vote + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &vote) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return vote +} + +// QueryGovVotes is terracli query gov votes +func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { + cmd := fmt.Sprintf("%s query gov votes %d %v", f.TerracliBinary, proposalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var votes []gov.Vote + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &votes) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return votes +} + +// QueryGovDeposit is terracli query gov deposit +func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit { + cmd := fmt.Sprintf("%s query gov deposit %d %s %v", f.TerracliBinary, proposalID, depositor, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var deposit gov.Deposit + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &deposit) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return deposit +} + +// QueryGovDeposits is terracli query gov deposits +func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit { + cmd := fmt.Sprintf("%s query gov deposits %d %v", f.TerracliBinary, propsalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var deposits []gov.Deposit + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &deposits) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return deposits +} + +//___________________________________________________________________________________ +// query slashing + +// QuerySigningInfo returns the signing info for a validator +func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { + cmd := fmt.Sprintf("%s query slashing signing-info %s %s", f.TerracliBinary, val, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var sinfo slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(res), &sinfo) + require.NoError(f.T, err) + return sinfo +} + +// QuerySlashingParams is terracli query slashing params +func (f *Fixtures) QuerySlashingParams() slashing.Params { + cmd := fmt.Sprintf("%s query slashing params %s", f.TerracliBinary, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var params slashing.Params + err := cdc.UnmarshalJSON([]byte(res), ¶ms) + require.NoError(f.T, err) + return params +} + +//___________________________________________________________________________________ +// query distribution + +// QueryRewards returns the rewards of a delegator +func (f *Fixtures) QueryRewards(delAddr sdk.AccAddress, flags ...string) distribution.QueryDelegatorTotalRewardsResponse { + cmd := fmt.Sprintf("%s query distribution rewards %s %s", f.TerracliBinary, delAddr, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var rewards distribution.QueryDelegatorTotalRewardsResponse + err := cdc.UnmarshalJSON([]byte(res), &rewards) + require.NoError(f.T, err) + return rewards +} + +//___________________________________________________________________________________ +// query supply + +// QueryTotalSupply returns the total supply of coins +func (f *Fixtures) QueryTotalSupply(flags ...string) (totalSupply sdk.Coins) { + cmd := fmt.Sprintf("%s query supply total %s", f.TerracliBinary, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(res), &totalSupply) + require.NoError(f.T, err) + return totalSupply +} + +// QueryTotalSupplyOf returns the total supply of a given coin denom +func (f *Fixtures) QueryTotalSupplyOf(denom string, flags ...string) sdk.Int { + cmd := fmt.Sprintf("%s query supply total %s %s", f.TerracliBinary, denom, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var supplyOf sdk.Int + err := cdc.UnmarshalJSON([]byte(res), &supplyOf) + require.NoError(f.T, err) + return supplyOf +} + +//___________________________________________________________________________________ +// executors + +func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) { + require.True(t, executeWrite(t, cmdStr, writes...)) +} + +func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) { + exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) + return +} + +func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) { + proc := tests.GoExecuteT(t, cmdStr) + + // Enables use of interactive commands + for _, write := range writes { + _, err := proc.StdinPipe.Write([]byte(write + "\n")) + require.NoError(t, err) + } + + // Read both stdout and stderr from the process + stdout, stderr, err := proc.ReadAll() + if err != nil { + fmt.Println("Err on proc.ReadAll()", err, cmdStr) + } + + // Log output. + if len(stdout) > 0 { + t.Log("Stdout:", string(stdout)) + } + if len(stderr) > 0 { + t.Log("Stderr:", string(stderr)) + } + + // Wait for process to exit + proc.Wait() + + // Return succes, stdout, stderr + return proc.ExitState.Success(), string(stdout), string(stderr) +} + +//___________________________________________________________________________________ +// utils + +func addFlags(cmd string, flags []string) string { + for _, f := range flags { + cmd += " " + f + } + return strings.TrimSpace(cmd) +} + +func queryTags(tags []string) (out string) { + for _, tag := range tags { + out += tag + "&" + } + return strings.TrimSuffix(out, "&") +} + +// Write the given string to a new temporary file +func WriteToNewTempFile(t *testing.T, s string) *os.File { + fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_") + require.Nil(t, err) + _, err = fp.WriteString(s) + require.Nil(t, err) + return fp +} + +func marshalStdTx(t *testing.T, stdTx authtypes.StdTx) []byte { + cdc := app.MakeCodec() + bz, err := cdc.MarshalBinaryBare(stdTx) + require.NoError(t, err) + return bz +} + +func unmarshalStdTx(t *testing.T, s string) (stdTx authtypes.StdTx) { + cdc := app.MakeCodec() + require.Nil(t, cdc.UnmarshalJSON([]byte(s), &stdTx)) + return +} + +func unmarshalEstimateFeeResult(t *testing.T, s string) (result authutils.EstimateFeeResp) { + cdc := app.MakeCodec() + require.Nil(t, cdc.UnmarshalJSON([]byte(s), &result)) + return +} diff --git a/client/keys/.gitignore b/client/keys/.gitignore deleted file mode 100644 index 2744c9935..000000000 --- a/client/keys/.gitignore +++ /dev/null @@ -1 +0,0 @@ -keys/ \ No newline at end of file diff --git a/client/keys/add.go b/client/keys/add.go deleted file mode 100755 index 38a5c921e..000000000 --- a/client/keys/add.go +++ /dev/null @@ -1,308 +0,0 @@ -package keys - -import ( - "bytes" - "errors" - "fmt" - "os" - "sort" - - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/client" - sdkkeys "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - bip39 "github.com/cosmos/go-bip39" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/libs/cli" -) - -const ( - flagInteractive = "interactive" - flagRecover = "recover" - flagOldHdPath = "old-hd-path" - flagNoBackup = "no-backup" - flagDryRun = "dry-run" - flagAccount = "account" - flagIndex = "index" - flagMultisig = "multisig" - flagNoSort = "nosort" -) - -func addKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "add ", - Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to disk", - Long: `Derive a new private key and encrypt to disk. -Optionally specify a BIP39 mnemonic, a BIP39 passphrase to further secure the mnemonic, -and a bip32 HD path to derive a specific account. The key will be stored under the given name -and encrypted with the given password. The only input that is required is the encryption password. - -If run with -i, it will prompt the user for BIP44 path, BIP39 mnemonic, and passphrase. -The flag --recover allows one to recover a key from a seed passphrase. -If run with --dry-run, a key would be generated (or recovered) but not stored to the -local keystore. -Use the --pubkey flag to add arbitrary public keys to the keystore for constructing -multisig transactions. - -You can add a multisig key by passing the list of key names you want the public -key to be composed of to the --multisig flag and the minimum number of signatures -required through --multisig-threshold. The keys are sorted by address, unless -the flag --nosort is set. -`, - Args: cobra.ExactArgs(1), - RunE: runAddCmd, - } - cmd.Flags().StringSlice(flagMultisig, nil, "Construct and store a multisig public key (implies --pubkey)") - cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures. For use in conjunction with --multisig") - cmd.Flags().Bool(flagNoSort, false, "Keys passed to --multisig are taken in the order they're supplied") - cmd.Flags().String(sdkkeys.FlagPublicKey, "", "Parse a public key in bech32 format and save it to disk") - cmd.Flags().BoolP(flagInteractive, "i", false, "Interactively prompt user for BIP39 passphrase and mnemonic") - cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device") - cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") - cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") - cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") - cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation") - cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") - cmd.Flags().Bool(flagOldHdPath, false, "Recover key with old hd path") - cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") - return cmd -} - -/* -input - - bip39 mnemonic - - bip39 passphrase - - bip44 path - - local encryption password -output - - armor encrypted private key (saved to file) -*/ -func runAddCmd(_ *cobra.Command, args []string) error { - var kb keys.Keybase - var err error - var encryptPassword string - - buf := client.BufferStdin() - name := args[0] - - interactive := viper.GetBool(flagInteractive) - showMnemonic := !viper.GetBool(flagNoBackup) - - if viper.GetBool(flagDryRun) { - // we throw this away, so don't enforce args, - // we want to get a new random seed phrase quickly - kb = keys.NewInMemory() - encryptPassword = app.DefaultKeyPass - } else { - kb, err = sdkkeys.NewKeyBaseFromHomeFlag() - if err != nil { - return err - } - - _, err = kb.Get(name) - if err == nil { - // account exists, ask for user confirmation - if response, err2 := client.GetConfirmation( - fmt.Sprintf("override the existing name %s", name), buf); err2 != nil || !response { - return err2 - } - } - - multisigKeys := viper.GetStringSlice(flagMultisig) - if len(multisigKeys) != 0 { - var pks []crypto.PubKey - - multisigThreshold := viper.GetInt(flagMultiSigThreshold) - if err := validateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { - return err - } - - for _, keyname := range multisigKeys { - k, err := kb.Get(keyname) - if err != nil { - return err - } - pks = append(pks, k.GetPubKey()) - } - - // Handle --nosort - if !viper.GetBool(flagNoSort) { - sort.Slice(pks, func(i, j int) bool { - return bytes.Compare(pks[i].Address(), pks[j].Address()) < 0 - }) - } - - pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) - if _, err := kb.CreateMulti(name, pk); err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "Key %q saved to disk.\n", name) - return nil - } - - // ask for a password when generating a local key - if viper.GetString(sdkkeys.FlagPublicKey) == "" && !viper.GetBool(client.FlagUseLedger) { - encryptPassword, err = client.GetCheckPassword( - "Enter a passphrase to encrypt your key to disk:", - "Repeat the passphrase:", buf) - if err != nil { - return err - } - } - } - - if viper.GetString(sdkkeys.FlagPublicKey) != "" { - pk, err := sdk.GetAccPubKeyBech32(viper.GetString(sdkkeys.FlagPublicKey)) - if err != nil { - return err - } - _, err = kb.CreateOffline(name, pk) - if err != nil { - return err - } - return nil - } - - account := uint32(viper.GetInt(flagAccount)) - index := uint32(viper.GetInt(flagIndex)) - - // If we're using ledger, only thing we need is the path and the bech32 prefix. - if viper.GetBool(client.FlagUseLedger) { - bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() - info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index) - if err != nil { - return err - } - - return printCreate(info, false, "") - } - - // Get bip39 mnemonic - var mnemonic string - var bip39Passphrase string - - if interactive || viper.GetBool(flagRecover) { - bip39Message := "Enter your bip39 mnemonic" - if !viper.GetBool(flagRecover) { - bip39Message = "Enter your bip39 mnemonic, or hit enter to generate one." - } - - mnemonic, err = client.GetString(bip39Message, buf) - if err != nil { - return err - } - - if !bip39.IsMnemonicValid(mnemonic) { - return errors.New("invalid mnemonic") - } - } - - if len(mnemonic) == 0 { - // read entropy seed straight from crypto.Rand and convert to mnemonic - entropySeed, err := bip39.NewEntropy(mnemonicEntropySize) - if err != nil { - return err - } - - mnemonic, err = bip39.NewMnemonic(entropySeed[:]) - if err != nil { - return err - } - } - - // override bip39 passphrase - if interactive { - bip39Passphrase, err = client.GetString( - "Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. "+ - "Most users should just hit enter to use the default, \"\"", buf) - if err != nil { - return err - } - - // if they use one, make them re-enter it - if len(bip39Passphrase) != 0 { - p2, err := client.GetString("Repeat the passphrase:", buf) - if err != nil { - return err - } - - if bip39Passphrase != p2 { - return errors.New("passphrases don't match") - } - } - } - - coinType := util.CoinType - if (viper.GetBool(flagRecover) || viper.GetBool(flagInteractive)) && viper.GetBool(flagOldHdPath) { - coinType = sdk.CoinType - } - - hdParams := hd.NewFundraiserParams(account, coinType, index) - info, err := kb.Derive(name, mnemonic, bip39Passphrase, encryptPassword, *hdParams) - if err != nil { - return err - } - - // Recover key from seed passphrase - if viper.GetBool(flagRecover) { - // Hide mnemonic from output - showMnemonic = false - mnemonic = "" - } - - return printCreate(info, showMnemonic, mnemonic) -} - -func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error { - output := viper.Get(cli.OutputFlag) - - switch output { - case sdkkeys.OutputFormatText: - fmt.Fprintln(os.Stderr) - printKeyInfo(info, keys.Bech32KeyOutput) - - // print mnemonic unless requested not to. - if showMnemonic { - fmt.Fprintln(os.Stderr, "\n**Important** write this mnemonic phrase in a safe place.") - fmt.Fprintln(os.Stderr, "It is the only way to recover your account if you ever forget your password.") - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, mnemonic) - } - case sdkkeys.OutputFormatJSON: - out, err := keys.Bech32KeyOutput(info) - if err != nil { - return err - } - - if showMnemonic { - out.Mnemonic = mnemonic - } - - var jsonString []byte - if viper.GetBool(client.FlagIndentResponse) { - jsonString, err = cdc.MarshalJSONIndent(out, "", " ") - } else { - jsonString, err = cdc.MarshalJSON(out) - } - - if err != nil { - return err - } - fmt.Fprintln(os.Stderr, string(jsonString)) - default: - return fmt.Errorf("I can't speak: %s", output) - } - - return nil -} diff --git a/client/keys/add_ledger_test.go b/client/keys/add_ledger_test.go deleted file mode 100755 index f32021c39..000000000 --- a/client/keys/add_ledger_test.go +++ /dev/null @@ -1,60 +0,0 @@ -//+build ledger,test_ledger_mock - -package keys - -import ( - "bufio" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/terra-project/core/testutil" - - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/cosmos/cosmos-sdk/tests" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/stretchr/testify/assert" -) - -func Test_runAddCmdLedger(t *testing.T) { - testutil.PrepareCmdTest() - - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - viper.Set(client.FlagUseLedger, true) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password - cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\ntest1234\n"))) - defer cleanUp1() - err := runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - // Now check that it has been stored properly - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - assert.NotNil(t, kb) - key1, err := kb.Get("keyname1") - assert.NoError(t, err) - assert.NotNil(t, key1) - - assert.Equal(t, "keyname1", key1.GetName()) - assert.Equal(t, keys.TypeLedger, key1.GetType()) - assert.Equal(t, - "terrapub1addwnpepqvpg7r26nl2pvqqern00m6s9uaax3hauu2rzg8qpjzq9hy6xve7sw0d84m6", - sdk.MustBech32ifyAccPub(key1.GetPubKey())) - - viper.Set(client.FlagUseLedger, false) -} diff --git a/client/keys/add_test.go b/client/keys/add_test.go deleted file mode 100755 index 948191301..000000000 --- a/client/keys/add_test.go +++ /dev/null @@ -1,289 +0,0 @@ -package keys - -import ( - "bufio" - "strings" - "testing" - - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/tests" - - "github.com/stretchr/testify/assert" - - "github.com/terra-project/core/testutil" -) - -func Test_runAddCmdBasic(t *testing.T) { - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password - cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\n"))) - defer cleanUp1() - err := runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - /// Test Text - Replace? >> FAIL - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\n"))) - defer cleanUp2() - err = runAddCmd(cmd, []string{"keyname1"}) - assert.Error(t, err) - - /// Test Text - Replace? Answer >> PASS - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password - cleanUp3 := client.OverrideStdin(bufio.NewReader(strings.NewReader("y\ntest1234\n"))) - defer cleanUp3() - err = runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - // Check JSON - viper.Set(cli.OutputFlag, OutputFormatJSON) - // Now enter password - cleanUp4 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\n"))) - defer cleanUp4() - err = runAddCmd(cmd, []string{"keyname2"}) - assert.NoError(t, err) - - recoverInitialViperState() -} - -func Test_runnAddCmdDryRun(t *testing.T) { - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - viper.Set(flagDryRun, true) - - keyName := "keyname1" - - // Without password - err := runAddCmd(cmd, []string{keyName}) - assert.NoError(t, err) - - // dry-run will not make any key info - _, err = GetKeyInfo(keyName) - assert.Error(t, err) - - recoverInitialViperState() -} - -func Test_runAddCmdRecover(t *testing.T) { - testutil.PrepareCmdTest() - - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - viper.Set(flagRecover, true) - viper.Set(flagOldHdPath, false) - - keyName := "keyname1" - password := "test1234\n" - mnemonic := "candy hint hamster cute inquiry bright industry decide assist wedding carpet fiber arm menu machine lottery type alert fan march argue adapt recycle stomach\n" - - // New HD Path - cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader(password + mnemonic))) - defer cleanUp1() - - err := runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - info, err := GetKeyInfo(keyName) - assert.NoError(t, err) - assert.Equal(t, "terra1wxuq9hkt4kes7r9kxh953l7p2cpcw8l73ek5dg", info.GetAddress().String()) - - // Old HD Path - viper.Set(flagOldHdPath, true) - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader("y\n" + password + mnemonic))) - defer cleanUp2() - - err = runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - info, err = GetKeyInfo(keyName) - assert.NoError(t, err) - assert.Equal(t, "terra1gaczd45crhwfa4x05k9747cuxwfmnduvmtyefs", info.GetAddress().String()) - - // recover with dry-run flag (default password) - viper.Set(flagDryRun, true) - viper.Set(flagOldHdPath, true) - - cleanUp3 := client.OverrideStdin(bufio.NewReader(strings.NewReader(mnemonic))) - defer cleanUp3() - - err = runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - recoverInitialViperState() -} - -func Test_runAddCmdPubkeyAndMultisig(t *testing.T) { - testutil.PrepareCmdTest() - - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - - /// Public Key Test - viper.Set(cli.OutputFlag, OutputFormatText) - viper.Set(flagRecover, true) - - pubkey1 := "terrapub1addwnpepqtmg9m7jy8xxqwnq05xh2rymsfph0mrfhzuz2lae3k09sn7qqwew7cgk76c" - pubkey2 := "terrapub1addwnpepqdn2knqsda3zxq4uv24yg5wp97e48sxdhuqyplmpya5eeujlm5zk5chrdt8" - pubkey3 := "terrapub1addwnpepqtycrza0rc9lk288gk9epwhdmft95t737vrctu75vp7h39l9rh24vxag39p" - - keyName1 := "keyname1" - keyName2 := "keyname2" - keyName3 := "keyname3" - - // Invalid Public Key - viper.Set(FlagPublicKey, "invalid") - - err := runAddCmd(cmd, []string{keyName1}) - assert.Error(t, err) - - // Valid - viper.Set(FlagPublicKey, pubkey1) - - err = runAddCmd(cmd, []string{keyName1}) - assert.NoError(t, err) - - info, err := GetKeyInfo(keyName1) - assert.NoError(t, err) - assert.Equal(t, "terra18smrf782hvjjeu3am06flc7nge2xvf8f2426q4", info.GetAddress().String()) - - viper.Set(FlagPublicKey, pubkey2) - - err = runAddCmd(cmd, []string{keyName2}) - assert.NoError(t, err) - - info, err = GetKeyInfo(keyName2) - assert.NoError(t, err) - assert.Equal(t, "terra1mzm0v94uchdufn806hxxzu6q4m3xclx2yzpdv8", info.GetAddress().String()) - - viper.Set(FlagPublicKey, pubkey3) - - err = runAddCmd(cmd, []string{keyName3}) - assert.NoError(t, err) - - info, err = GetKeyInfo(keyName3) - assert.NoError(t, err) - assert.Equal(t, "terra1yr0sqzfraffdwv9c33gx2dqhcl525muheuefaf", info.GetAddress().String()) - - // Multisig Test - keyNameMultisig := "keyNameMultisig" - viper.Set(FlagPublicKey, "") - viper.Set(flagMultisig, keyName1+" "+keyName2+" "+keyName3) - - // Invalid Threashold - viper.Set(flagMultiSigThreshold, 0) - err = runAddCmd(cmd, []string{keyNameMultisig}) - assert.Error(t, err) - - // Invalid Key Name - viper.Set(flagMultiSigThreshold, 2) - viper.Set(flagMultisig, keyName1+" "+keyName2+" hihi") - err = runAddCmd(cmd, []string{keyNameMultisig}) - assert.Error(t, err) - - // Valid - viper.Set(flagMultisig, keyName1+" "+keyName2+" "+keyName3) - err = runAddCmd(cmd, []string{keyNameMultisig}) - assert.NoError(t, err) - - info, err = GetKeyInfo(keyNameMultisig) - assert.NoError(t, err) - assert.Equal(t, "terra1tswgrqcdauaw06dxeycj8ctr5etlah6aqg7elm", info.GetAddress().String()) - - recoverInitialViperState() -} - -func Test_runAddCmdInteractive(t *testing.T) { - testutil.PrepareCmdTest() - - cmd := addKeyCommand() - assert.NotNil(t, cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(cli.HomeFlag, kbHome) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - viper.Set(flagInteractive, true) - viper.Set(flagOldHdPath, false) - - keyName := "keyname1" - password := "test1234\n" - mnemonic := "candy hint hamster cute inquiry bright industry decide assist wedding carpet fiber arm menu machine lottery type alert fan march argue adapt recycle stomach\n" - bip39Passphrase := "hihi\nhihi\n" - - // New HD path - cleanUp1 := client.OverrideStdin(bufio.NewReader(strings.NewReader(password + mnemonic + bip39Passphrase))) - defer cleanUp1() - - err := runAddCmd(cmd, []string{keyName}) - assert.NoError(t, err) - - info, err := GetKeyInfo(keyName) - assert.NoError(t, err) - assert.Equal(t, "terra1smea3fuwun5ggfjep25gd7yv8kvw3mvx2hw3zm", info.GetAddress().String()) - - viper.Set(flagOldHdPath, true) - // Old HD path - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader("y\n" + password + mnemonic + bip39Passphrase))) - defer cleanUp2() - - err = runAddCmd(cmd, []string{keyName}) - assert.NoError(t, err) - - info, err = GetKeyInfo(keyName) - assert.NoError(t, err) - assert.Equal(t, "terra1nv4nsd7tfl8xc2dm7rry5exwcf350wjguk0x2c", info.GetAddress().String()) - - recoverInitialViperState() -} - -func recoverInitialViperState() { - viper.Set(flagInteractive, false) - viper.Set(flagRecover, false) - viper.Set(FlagPublicKey, "") - viper.Set(flagMultisig, "") - viper.Set(flagMultiSigThreshold, 0) - viper.Set(flagDryRun, false) -} diff --git a/client/keys/codec.go b/client/keys/codec.go deleted file mode 100755 index 6bbb16850..000000000 --- a/client/keys/codec.go +++ /dev/null @@ -1,22 +0,0 @@ -package keys - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var cdc *codec.Codec - -func init() { - cdc = codec.New() - codec.RegisterCrypto(cdc) -} - -// marshal keys -func MarshalJSON(o interface{}) ([]byte, error) { - return cdc.MarshalJSON(o) -} - -// unmarshal json -func UnmarshalJSON(bz []byte, ptr interface{}) error { - return cdc.UnmarshalJSON(bz, ptr) -} diff --git a/client/keys/codec_test.go b/client/keys/codec_test.go deleted file mode 100755 index a30aea30a..000000000 --- a/client/keys/codec_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package keys - -import ( - "fmt" - "reflect" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/crypto/keys" -) - -type testCases struct { - Keys []keys.KeyOutput - Answers []keys.KeyOutput - JSON [][]byte -} - -func getTestCases() testCases { - return testCases{ - []keys.KeyOutput{ - {Name: "A", Type: "B", Address: "C", PubKey: "D", Mnemonic: "E", Threshold: 0, PubKeys: nil}, - {Name: "A", Type: "B", Address: "C", PubKey: "D", Mnemonic: "", Threshold: 0, PubKeys: nil}, - {Name: "", Type: "B", Address: "C", PubKey: "D", Mnemonic: "", Threshold: 0, PubKeys: nil}, - {Name: "", Type: "", Address: "", PubKey: "", Mnemonic: "", Threshold: 0, PubKeys: nil}, - }, - make([]keys.KeyOutput, 4), - [][]byte{ - []byte(`{"name":"A","type":"B","address":"C","pubkey":"D","mnemonic":"E"}`), - []byte(`{"name":"A","type":"B","address":"C","pubkey":"D"}`), - []byte(`{"name":"","type":"B","address":"C","pubkey":"D"}`), - []byte(`{"name":"","type":"","address":"","pubkey":""}`), - }, - } -} - -func TestMarshalJSON(t *testing.T) { - type args struct { - o keys.KeyOutput - } - - data := getTestCases() - - tests := []struct { - name string - args args - want []byte - wantErr bool - }{ - {"basic", args{data.Keys[0]}, []byte(data.JSON[0]), false}, - {"mnemonic is optional", args{data.Keys[1]}, []byte(data.JSON[1]), false}, - - // REVIEW: Are the next results expected?? - {"empty name", args{data.Keys[2]}, []byte(data.JSON[2]), false}, - {"empty object", args{data.Keys[3]}, []byte(data.JSON[3]), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := MarshalJSON(tt.args.o) - if (err != nil) != tt.wantErr { - t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - fmt.Printf("%s\n", got) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestUnmarshalJSON(t *testing.T) { - type args struct { - bz []byte - ptr interface{} - } - - data := getTestCases() - - tests := []struct { - name string - args args - wantErr bool - }{ - {"basic", args{data.JSON[0], &data.Answers[0]}, false}, - {"mnemonic is optional", args{data.JSON[1], &data.Answers[1]}, false}, - - // REVIEW: Are the next results expected?? - {"empty name", args{data.JSON[2], &data.Answers[2]}, false}, - {"empty object", args{data.JSON[3], &data.Answers[3]}, false}, - } - for idx, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := UnmarshalJSON(tt.args.bz, tt.args.ptr); (err != nil) != tt.wantErr { - t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - - // Confirm deserialized objects are the same - require.Equal(t, data.Keys[idx], data.Answers[idx]) - }) - } -} diff --git a/client/keys/delete.go b/client/keys/delete.go deleted file mode 100755 index 5ae96aab9..000000000 --- a/client/keys/delete.go +++ /dev/null @@ -1,98 +0,0 @@ -package keys - -import ( - "bufio" - "errors" - "fmt" - "os" - - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/crypto/keys" - - "github.com/spf13/cobra" -) - -const ( - flagYes = "yes" - flagForce = "force" -) - -func deleteKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "delete ", - Short: "Delete the given key", - Long: `Delete a key from the store. - -Note that removing offline or ledger keys will remove -only the public key references stored locally, i.e. -private keys stored in a ledger device cannot be deleted with -gaiacli. -`, - RunE: runDeleteCmd, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().BoolP(flagYes, "y", false, - "Skip confirmation prompt when deleting offline or ledger key references") - cmd.Flags().BoolP(flagForce, "f", false, - "Remove the key unconditionally without asking for the passphrase") - return cmd -} - -func runDeleteCmd(cmd *cobra.Command, args []string) error { - name := args[0] - - kb, err := NewKeyBaseFromHomeFlag() - if err != nil { - return err - } - - info, err := kb.Get(name) - if err != nil { - return err - } - - buf := client.BufferStdin() - if info.GetType() == keys.TypeLedger || info.GetType() == keys.TypeOffline || info.GetType() == keys.TypeMulti { - if !viper.GetBool(flagYes) { - if err := confirmDeletion(buf); err != nil { - return err - } - } - if err := kb.Delete(name, "", true); err != nil { - return err - } - fmt.Fprintln(os.Stderr, "Public key reference deleted") - return nil - } - - // skip passphrase check if run with --force - skipPass := viper.GetBool(flagForce) - var oldpass string - if !skipPass { - if oldpass, err = client.GetPassword( - "DANGER - enter password to permanently delete key:", buf); err != nil { - return err - } - } - - err = kb.Delete(name, oldpass, skipPass) - if err != nil { - return err - } - fmt.Fprintln(os.Stderr, "Key deleted forever (uh oh!)") - return nil -} - -func confirmDeletion(buf *bufio.Reader) error { - answer, err := client.GetConfirmation("Key reference will be deleted. Continue?", buf) - if err != nil { - return err - } - if !answer { - return errors.New("aborted") - } - return nil -} diff --git a/client/keys/delete_test.go b/client/keys/delete_test.go deleted file mode 100755 index c388e02b7..000000000 --- a/client/keys/delete_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package keys - -import ( - "bufio" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/cosmos/cosmos-sdk/tests" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/cli" -) - -func Test_runDeleteCmd(t *testing.T) { - deleteKeyCommand := deleteKeyCommand() - - yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes) - forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce) - assert.False(t, yesF) - assert.False(t, forceF) - - fakeKeyName1 := "runDeleteCmd_Key1" - fakeKeyName2 := "runDeleteCmd_Key2" - fakeKeyName3 := "runDeleteCmd_Key3" - fakeKeyName4 := "runDeleteCmd_Key4" - - // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(cli.HomeFlag, kbHome) - - // Now - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName3, tests.TestMnemonic, "", "test1234", 0, 0) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName4, tests.TestMnemonic, "", "test1234", 0, 0) - assert.NoError(t, err) - - err = runDeleteCmd(deleteKeyCommand, []string{"blah"}) - require.Error(t, err) - require.Equal(t, "Key blah not found", err.Error()) - - // User confirmation missing - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}) - require.Error(t, err) - require.Equal(t, "EOF", err.Error()) - - { - _, err = kb.Get(fakeKeyName1) - require.NoError(t, err) - - // Now there is a confirmation - cleanUp := client.OverrideStdin(bufio.NewReader(strings.NewReader("y\n"))) - defer cleanUp() - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}) - require.NoError(t, err) - - _, err = kb.Get(fakeKeyName1) - require.Error(t, err) // Key1 is gone - } - - viper.Set(flagYes, true) - _, err = kb.Get(fakeKeyName2) - require.NoError(t, err) - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2}) - require.NoError(t, err) - _, err = kb.Get(fakeKeyName2) - require.Error(t, err) // Key2 is gone - - // Invalid Password - viper.Set(flagYes, false) - _, err = kb.Get(fakeKeyName3) - require.NoError(t, err) - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader("invalid\n"))) - defer cleanUp2() - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName3}) - require.Error(t, err) - _, err = kb.Get(fakeKeyName3) - require.NoError(t, err) // Key3 is not gone - - // Valid Password - cleanUp3 := client.OverrideStdin(bufio.NewReader(strings.NewReader("test1234\n"))) - defer cleanUp3() - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName3}) - require.NoError(t, err) - _, err = kb.Get(fakeKeyName3) - require.Error(t, err) // Key3 is gone - - // Force Delete - viper.Set(flagForce, true) - _, err = kb.Get(fakeKeyName4) - require.NoError(t, err) - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName4}) - require.NoError(t, err) - _, err = kb.Get(fakeKeyName4) - require.Error(t, err) // Key4 is gone - - // TODO: Write another case for !keys.Local -} - -func Test_confirmDeletion(t *testing.T) { - type args struct { - buf *bufio.Reader - } - - answerYes := bufio.NewReader(strings.NewReader("y\n")) - answerYes2 := bufio.NewReader(strings.NewReader("Y\n")) - answerNo := bufio.NewReader(strings.NewReader("n\n")) - answerInvalid := bufio.NewReader(strings.NewReader("245\n")) - - tests := []struct { - name string - args args - wantErr bool - }{ - {"Y", args{answerYes}, false}, - {"y", args{answerYes2}, false}, - {"N", args{answerNo}, true}, - {"BAD", args{answerInvalid}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := confirmDeletion(tt.args.buf); (err != nil) != tt.wantErr { - t.Errorf("confirmDeletion() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/client/keys/errors.go b/client/keys/errors.go deleted file mode 100755 index a603b1d1a..000000000 --- a/client/keys/errors.go +++ /dev/null @@ -1,31 +0,0 @@ -package keys - -import "fmt" - -func errKeyNameConflict(name string) error { - return fmt.Errorf("account with name %s already exists", name) -} - -func errMissingName() error { - return fmt.Errorf("you have to specify a name for the locally stored account") -} - -func errMissingPassword() error { - return fmt.Errorf("you have to specify a password for the locally stored account") -} - -func errMissingMnemonic() error { - return fmt.Errorf("you have to specify a mnemonic for key recovery") -} - -func errInvalidMnemonic() error { - return fmt.Errorf("the mnemonic is invalid") -} - -func errInvalidAccountNumber() error { - return fmt.Errorf("the account number is invalid") -} - -func errInvalidIndexNumber() error { - return fmt.Errorf("the index number is invalid") -} diff --git a/client/keys/list.go b/client/keys/list.go deleted file mode 100755 index 19873aabb..000000000 --- a/client/keys/list.go +++ /dev/null @@ -1,31 +0,0 @@ -package keys - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" -) - -func listKeysCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Short: "List all keys", - Long: `Return a list of all public keys stored by this key manager -along with their associated name and address.`, - RunE: runListCmd, - } - cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") - return cmd -} - -func runListCmd(cmd *cobra.Command, args []string) error { - kb, err := NewKeyBaseFromHomeFlag() - if err != nil { - return err - } - - infos, err := kb.List() - if err == nil { - printInfos(infos) - } - return err -} diff --git a/client/keys/list_test.go b/client/keys/list_test.go deleted file mode 100755 index 9fdab6a75..000000000 --- a/client/keys/list_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package keys - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/tests" - "github.com/stretchr/testify/assert" - - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/spf13/cobra" -) - -func Test_runListCmd(t *testing.T) { - type args struct { - cmd *cobra.Command - args []string - } - - cmdBasic := listKeysCmd() - - // Prepare some keybases - kbHome1, cleanUp1 := tests.NewTestCaseDir(t) - defer cleanUp1() - // Do nothing, leave home1 empty - - kbHome2, cleanUp2 := tests.NewTestCaseDir(t) - defer cleanUp2() - viper.Set(cli.HomeFlag, kbHome2) - - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) - - testData := []struct { - name string - kbDir string - args args - wantErr bool - }{ - {"invalid keybase", "/dev/null", args{cmdBasic, []string{}}, true}, - {"keybase: empty", kbHome1, args{cmdBasic, []string{}}, false}, - {"keybase: w/key", kbHome2, args{cmdBasic, []string{}}, false}, - } - for _, tt := range testData { - t.Run(tt.name, func(t *testing.T) { - viper.Set(cli.HomeFlag, tt.kbDir) - if err := runListCmd(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr { - t.Errorf("runListCmd() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/client/keys/mnemonic.go b/client/keys/mnemonic.go deleted file mode 100755 index b9d434d2b..000000000 --- a/client/keys/mnemonic.go +++ /dev/null @@ -1,75 +0,0 @@ -package keys - -import ( - "crypto/sha256" - "fmt" - - bip39 "github.com/bartekn/go-bip39" - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" -) - -const ( - flagUserEntropy = "unsafe-entropy" - - mnemonicEntropySize = 256 -) - -func mnemonicKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "mnemonic", - Short: "Compute the bip39 mnemonic for some input entropy", - Long: "Create a bip39 mnemonic, sometimes called a seed phrase, by reading from the system entropy. To pass your own entropy, use --unsafe-entropy", - RunE: runMnemonicCmd, - } - cmd.Flags().Bool(flagUserEntropy, false, "Prompt the user to supply their own entropy, instead of relying on the system") - return cmd -} - -func runMnemonicCmd(cmd *cobra.Command, args []string) error { - flags := cmd.Flags() - - userEntropy, _ := flags.GetBool(flagUserEntropy) - - var entropySeed []byte - - if userEntropy { - // prompt the user to enter some entropy - buf := client.BufferStdin() - inputEntropy, err := client.GetString("> WARNING: Generate at least 256-bits of entropy and enter the results here:", buf) - if err != nil { - return err - } - if len(inputEntropy) < 43 { - return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy)) - } - conf, err := client.GetConfirmation(fmt.Sprintf("> Input length: %d", len(inputEntropy)), buf) - if err != nil { - return err - } - if !conf { - return nil - } - - // hash input entropy to get entropy seed - hashedEntropy := sha256.Sum256([]byte(inputEntropy)) - entropySeed = hashedEntropy[:] - } else { - // read entropy seed straight from crypto.Rand - var err error - entropySeed, err = bip39.NewEntropy(mnemonicEntropySize) - if err != nil { - return err - } - } - - mnemonic, err := bip39.NewMnemonic(entropySeed[:]) - if err != nil { - return err - } - - fmt.Println(mnemonic) - - return nil -} diff --git a/client/keys/mnemonic_test.go b/client/keys/mnemonic_test.go deleted file mode 100755 index 64fd404a1..000000000 --- a/client/keys/mnemonic_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package keys - -import ( - "bufio" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/stretchr/testify/assert" - - "github.com/stretchr/testify/require" -) - -func Test_RunMnemonicCmdNormal(t *testing.T) { - cmdBasic := mnemonicKeyCommand() - err := runMnemonicCmd(cmdBasic, []string{}) - require.NoError(t, err) -} - -func Test_RunMnemonicCmdUser(t *testing.T) { - cmdUser := mnemonicKeyCommand() - err := cmdUser.Flags().Set(flagUserEntropy, "1") - assert.NoError(t, err) - - err = runMnemonicCmd(cmdUser, []string{}) - require.Error(t, err) - require.Equal(t, "EOF", err.Error()) - - // Try again - cleanUp := client.OverrideStdin(bufio.NewReader(strings.NewReader("Hi!\n"))) - defer cleanUp() - err = runMnemonicCmd(cmdUser, []string{}) - require.Error(t, err) - require.Equal(t, - "256-bits is 43 characters in Base-64, and 100 in Base-6. You entered 3, and probably want more", - err.Error()) - - // Now provide "good" entropy :) - fakeEntropy := strings.Repeat(":)", 40) + "\ny\n" // entropy + accept count - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader(fakeEntropy))) - defer cleanUp2() - err = runMnemonicCmd(cmdUser, []string{}) - require.NoError(t, err) - - // Now provide "good" entropy but no answer - fakeEntropy = strings.Repeat(":)", 40) + "\n" // entropy + accept count - cleanUp3 := client.OverrideStdin(bufio.NewReader(strings.NewReader(fakeEntropy))) - defer cleanUp3() - err = runMnemonicCmd(cmdUser, []string{}) - require.Error(t, err) - - // Now provide "good" entropy but say no - fakeEntropy = strings.Repeat(":)", 40) + "\nn\n" // entropy + accept count - cleanUp4 := client.OverrideStdin(bufio.NewReader(strings.NewReader(fakeEntropy))) - defer cleanUp4() - err = runMnemonicCmd(cmdUser, []string{}) - require.NoError(t, err) -} diff --git a/client/keys/root.go b/client/keys/root.go deleted file mode 100755 index e49b57d4a..000000000 --- a/client/keys/root.go +++ /dev/null @@ -1,31 +0,0 @@ -package keys - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" -) - -// Commands registers a sub-tree of commands to interact with -// local private key storage. -func Commands() *cobra.Command { - cmd := &cobra.Command{ - Use: "keys", - Short: "Add or view local private keys", - Long: `Keys allows you to manage your local keystore for tendermint. - - These keys may be in any format supported by go-crypto and can be - used by light-clients, full nodes, or any other application that - needs to sign with a private key.`, - } - cmd.AddCommand( - mnemonicKeyCommand(), - addKeyCommand(), - listKeysCmd(), - showKeysCmd(), - client.LineBreak, - deleteKeyCommand(), - updateKeyCommand(), - ) - return cmd -} diff --git a/client/keys/root_test.go b/client/keys/root_test.go deleted file mode 100755 index 1cc2db425..000000000 --- a/client/keys/root_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package keys - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCommands(t *testing.T) { - rootCommands := Commands() - assert.NotNil(t, rootCommands) - - // Commands are registered - assert.Equal(t, 7, len(rootCommands.Commands())) -} diff --git a/client/keys/show.go b/client/keys/show.go deleted file mode 100755 index 8f87482e0..000000000 --- a/client/keys/show.go +++ /dev/null @@ -1,167 +0,0 @@ -package keys - -import ( - "errors" - "fmt" - "github.com/cosmos/cosmos-sdk/client" - - "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - tmcrypto "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/libs/cli" -) - -const ( - // FlagAddress is the flag for the user's address on the command line. - FlagAddress = "address" - // FlagPublicKey represents the user's public key on the command line. - FlagPublicKey = "pubkey" - // FlagBechPrefix defines a desired Bech32 prefix encoding for a key. - FlagBechPrefix = "bech" - // FlagDevice indicates that the information should be shown in the device - FlagDevice = "device" - - flagMultiSigThreshold = "multisig-threshold" - flagShowMultiSig = "show-multisig" - - defaultMultiSigKeyName = "multi" -) - -func showKeysCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "show [name [name...]]", - Short: "Show key info for the given name", - Long: `Return public details of a single local key. If multiple names are -provided, then an ephemeral multisig key will be created under the name "multi" -consisting of all the keys provided by name and multisig threshold.`, - Args: cobra.MinimumNArgs(1), - RunE: runShowCmd, - } - - cmd.Flags().String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)") - cmd.Flags().BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)") - cmd.Flags().BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)") - cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in a ledger device") - cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures") - cmd.Flags().BoolP(flagShowMultiSig, "m", false, "Output multisig pubkey constituents, threshold, and weights") - cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") - - return cmd -} - -func runShowCmd(cmd *cobra.Command, args []string) (err error) { - var info keys.Info - - if len(args) == 1 { - info, err = GetKeyInfo(args[0]) - if err != nil { - return err - } - } else { - pks := make([]tmcrypto.PubKey, len(args)) - for i, keyName := range args { - info, err := GetKeyInfo(keyName) - if err != nil { - return err - } - - pks[i] = info.GetPubKey() - } - - multisigThreshold := viper.GetInt(flagMultiSigThreshold) - err = validateMultisigThreshold(multisigThreshold, len(args)) - if err != nil { - return err - } - - multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) - info = keys.NewMultiInfo(defaultMultiSigKeyName, multikey) - } - - isShowAddr := viper.GetBool(FlagAddress) - isShowPubKey := viper.GetBool(FlagPublicKey) - isShowDevice := viper.GetBool(FlagDevice) - isShowMultiSig := viper.GetBool(flagShowMultiSig) - - isOutputSet := false - tmp := cmd.Flag(cli.OutputFlag) - if tmp != nil { - isOutputSet = tmp.Changed - } - - if isShowAddr && isShowPubKey { - return errors.New("cannot use both --address and --pubkey at once") - } - - if isOutputSet && (isShowAddr || isShowPubKey) { - return errors.New("cannot use --output with --address or --pubkey") - } - - bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix)) - if err != nil { - return err - } - - switch { - case isShowAddr: - printKeyAddress(info, bechKeyOut) - case isShowPubKey: - printPubKey(info, bechKeyOut) - case isShowMultiSig: - printMultiSigKeyInfo(info, bechKeyOut) - default: - printKeyInfo(info, bechKeyOut) - } - - if isShowDevice { - if isShowPubKey { - return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys") - } - if viper.GetString(FlagBechPrefix) != "acc" { - return fmt.Errorf("the device flag (-d) can only be used for accounts") - } - // Override and show in the device - if info.GetType() != keys.TypeLedger { - return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices") - } - - hdpath, err := info.GetPath() - if err != nil { - return nil - } - - return crypto.LedgerShowAddress(*hdpath, info.GetPubKey()) - } - - return nil -} - -func validateMultisigThreshold(k, nKeys int) error { - if k <= 0 { - return fmt.Errorf("threshold must be a positive integer") - } - if nKeys < k { - return fmt.Errorf( - "threshold k of n multisignature: %d < %d", nKeys, k) - } - return nil -} - -func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { - switch bechPrefix { - case sdk.PrefixAccount: - return keys.Bech32KeyOutput, nil - case sdk.PrefixValidator: - return keys.Bech32ValKeyOutput, nil - case sdk.PrefixConsensus: - return keys.Bech32ConsKeyOutput, nil - } - - return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix) -} diff --git a/client/keys/show_test.go b/client/keys/show_test.go deleted file mode 100755 index 26f438eb7..000000000 --- a/client/keys/show_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package keys - -import ( - "testing" - - "github.com/terra-project/core/testutil" - - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/tendermint/tendermint/libs/cli" -) - -func Test_multiSigKey_Properties(t *testing.T) { - testutil.PrepareCmdTest() - - tmpKey1 := secp256k1.GenPrivKeySecp256k1([]byte("mySecret")) - pk := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey1.PubKey()}) - tmp := keys.NewMultiInfo("myMultisig", pk) - - assert.Equal(t, "myMultisig", tmp.GetName()) - assert.Equal(t, keys.TypeMulti, tmp.GetType()) - assert.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String()) - assert.Equal(t, "terra16wfryel63g7axeamw68630wglalcnk3lfxxa0c", tmp.GetAddress().String()) -} - -func Test_showKeysCmd(t *testing.T) { - cmd := showKeysCmd() - assert.NotNil(t, cmd) - assert.Equal(t, "false", cmd.Flag(FlagAddress).DefValue) - assert.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue) -} - -func Test_runShowCmd(t *testing.T) { - cmd := showKeysCmd() - - err := runShowCmd(cmd, []string{"invalid"}) - assert.EqualError(t, err, "Key invalid not found") - - err = runShowCmd(cmd, []string{"invalid1", "invalid2"}) - assert.EqualError(t, err, "Key invalid1 not found") - - // Prepare a key base - // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(cli.HomeFlag, kbHome) - - fakeKeyName1 := "runShowCmd_Key1" - fakeKeyName2 := "runShowCmd_Key2" - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) - assert.NoError(t, err) - - // Now try single key - err = runShowCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "invalid Bech32 prefix encoding provided: ") - - // Now try single key - set bech to acc - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - err = runShowCmd(cmd, []string{fakeKeyName1}) - assert.NoError(t, err) - - // Now try multisig key - set bech to acc - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - viper.Set(flagMultiSigThreshold, 0) - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.EqualError(t, err, "threshold must be a positive integer") - - // Now try multisig key - set bech to acc + threshold=2 - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - viper.Set(flagMultiSigThreshold, 2) - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.NoError(t, err) - - // Now try multisig key - set bech to acc + threshold=2 - viper.Set(FlagBechPrefix, "acc") - viper.Set(FlagDevice, true) - viper.Set(flagMultiSigThreshold, 2) - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices") - - viper.Set(FlagBechPrefix, "val") - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.EqualError(t, err, "the device flag (-d) can only be used for accounts") - - viper.Set(FlagPublicKey, true) - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - assert.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys") - - // TODO: Capture stdout and compare -} - -func Test_validateMultisigThreshold(t *testing.T) { - type args struct { - k int - nKeys int - } - tests := []struct { - name string - args args - wantErr bool - }{ - {"zeros", args{0, 0}, true}, - {"1-0", args{1, 0}, true}, - {"1-1", args{1, 1}, false}, - {"1-2", args{1, 1}, false}, - {"1-2", args{2, 1}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := validateMultisigThreshold(tt.args.k, tt.args.nKeys); (err != nil) != tt.wantErr { - t.Errorf("validateMultisigThreshold() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func Test_getBechKeyOut(t *testing.T) { - type args struct { - bechPrefix string - } - tests := []struct { - name string - args args - want bechKeyOutFn - wantErr bool - }{ - {"empty", args{""}, nil, true}, - {"wrong", args{"???"}, nil, true}, - {"acc", args{sdk.PrefixAccount}, keys.Bech32KeyOutput, false}, - {"val", args{sdk.PrefixValidator}, keys.Bech32ValKeyOutput, false}, - {"cons", args{sdk.PrefixConsensus}, keys.Bech32ConsKeyOutput, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := getBechKeyOut(tt.args.bechPrefix) - if (err != nil) != tt.wantErr { - t.Errorf("getBechKeyOut() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if !tt.wantErr { - assert.NotNil(t, got) - } - - // TODO: Still not possible to compare functions - // Maybe in next release: https://github.com/stretchr/testify/issues/182 - //if &got != &tt.want { - // t.Errorf("getBechKeyOut() = %v, want %v", got, tt.want) - //} - }) - } -} diff --git a/client/keys/types.go b/client/keys/types.go deleted file mode 100755 index 079ef4962..000000000 --- a/client/keys/types.go +++ /dev/null @@ -1,55 +0,0 @@ -package keys - -// used for outputting keys.Info over REST - -// AddNewKey request a new key -type AddNewKey struct { - Name string `json:"name"` - Password string `json:"password"` - Mnemonic string `json:"mnemonic"` - Account int `json:"account,string,omitempty"` - Index int `json:"index,string,omitempty"` -} - -// NewAddNewKey constructs a new AddNewKey request structure. -func NewAddNewKey(name, password, mnemonic string, account, index int) AddNewKey { - return AddNewKey{ - Name: name, - Password: password, - Mnemonic: mnemonic, - Account: account, - Index: index, - } -} - -// RecoverKeyBody recovers a key -type RecoverKey struct { - Password string `json:"password"` - Mnemonic string `json:"mnemonic"` - Account int `json:"account,string,omitempty"` - Index int `json:"index,string,omitempty"` -} - -// NewRecoverKey constructs a new RecoverKey request structure. -func NewRecoverKey(password, mnemonic string, account, index int) RecoverKey { - return RecoverKey{Password: password, Mnemonic: mnemonic, Account: account, Index: index} -} - -// UpdateKeyReq requests updating a key -type UpdateKeyReq struct { - OldPassword string `json:"old_password"` - NewPassword string `json:"new_password"` -} - -// NewUpdateKeyReq constructs a new UpdateKeyReq structure. -func NewUpdateKeyReq(old, new string) UpdateKeyReq { - return UpdateKeyReq{OldPassword: old, NewPassword: new} -} - -// DeleteKeyReq requests deleting a key -type DeleteKeyReq struct { - Password string `json:"password"` -} - -// NewDeleteKeyReq constructs a new DeleteKeyReq structure. -func NewDeleteKeyReq(password string) DeleteKeyReq { return DeleteKeyReq{Password: password} } diff --git a/client/keys/update.go b/client/keys/update.go deleted file mode 100755 index 392286d48..000000000 --- a/client/keys/update.go +++ /dev/null @@ -1,47 +0,0 @@ -package keys - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/spf13/cobra" -) - -func updateKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "update ", - Short: "Change the password used to protect private key", - RunE: runUpdateCmd, - Args: cobra.ExactArgs(1), - } - return cmd -} - -func runUpdateCmd(cmd *cobra.Command, args []string) error { - name := args[0] - - buf := client.BufferStdin() - kb, err := NewKeyBaseFromHomeFlag() - if err != nil { - return err - } - oldpass, err := client.GetPassword( - "Enter the current passphrase:", buf) - if err != nil { - return err - } - - getNewpass := func() (string, error) { - return client.GetCheckPassword( - "Enter the new passphrase:", - "Repeat the new passphrase:", buf) - } - - err = kb.Update(name, oldpass, getNewpass) - if err != nil { - return err - } - fmt.Println("Password successfully updated!") - return nil -} diff --git a/client/keys/update_test.go b/client/keys/update_test.go deleted file mode 100755 index e1c6784f1..000000000 --- a/client/keys/update_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package keys - -import ( - "bufio" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/cosmos/cosmos-sdk/tests" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/stretchr/testify/assert" -) - -func Test_updateKeyCommand(t *testing.T) { - cmd := updateKeyCommand() - assert.NotNil(t, cmd) - // No flags or defaults to validate -} - -func Test_runUpdateCmd(t *testing.T) { - fakeKeyName1 := "runUpdateCmd_Key1" - fakeKeyName2 := "runUpdateCmd_Key2" - - cmd := updateKeyCommand() - - // fails because it requests a password - err := runUpdateCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "EOF") - - cleanUp := client.OverrideStdin(bufio.NewReader(strings.NewReader("pass1234\n"))) - defer cleanUp() - - // try again - err = runUpdateCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "Key runUpdateCmd_Key1 not found") - - // Prepare a key base - // Now add a temporary keybase - kbHome, cleanUp1 := tests.NewTestCaseDir(t) - defer cleanUp1() - viper.Set(cli.HomeFlag, kbHome) - - kb, err := NewKeyBaseFromHomeFlag() - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) - assert.NoError(t, err) - - // Try again now that we have keys - cleanUp2 := client.OverrideStdin(bufio.NewReader(strings.NewReader("pass1234\nNew1234\nNew1234"))) - defer cleanUp2() - - // Incorrect key type - err = runUpdateCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "locally stored key required. Received: keys.offlineInfo") - - // TODO: Check for other type types? - -} diff --git a/client/keys/utils.go b/client/keys/utils.go deleted file mode 100755 index 5af2f53e3..000000000 --- a/client/keys/utils.go +++ /dev/null @@ -1,152 +0,0 @@ -package keys - -import ( - "fmt" - "path/filepath" - - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/crypto/keys" -) - -// available output formats. -const ( - OutputFormatText = "text" - OutputFormatJSON = "json" - - // defaultKeyDBName is the client's subdirectory where keys are stored. - defaultKeyDBName = "keys" -) - -type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error) - -// GetKeyInfo returns key info for a given name. An error is returned if the -// keybase cannot be retrieved or getting the info fails. -func GetKeyInfo(name string) (keys.Info, error) { - keybase, err := NewKeyBaseFromHomeFlag() - if err != nil { - return nil, err - } - - return keybase.Get(name) -} - -// NewKeyBaseFromHomeFlag initializes a Keybase based on the configuration. -func NewKeyBaseFromHomeFlag() (keys.Keybase, error) { - rootDir := viper.GetString(cli.HomeFlag) - return NewKeyBaseFromDir(rootDir) -} - -// NewKeyBaseFromDir initializes a keybase at a particular dir. -func NewKeyBaseFromDir(rootDir string) (keys.Keybase, error) { - return getLazyKeyBaseFromDir(rootDir) -} - -func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) { - return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil -} - -func printKeyTextHeader() { - fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\tPUBKEY:\n") -} - -func printMultiSigKeyTextHeader() { - fmt.Printf("WEIGHT:\tTHRESHOLD:\tADDRESS:\t\t\t\t\tPUBKEY:\n") -} - -func printMultiSigKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) { - ko, err := bechKeyOut(keyInfo) - if err != nil { - panic(err) - } - - printMultiSigKeyTextHeader() - printMultiSigKeyOutput(ko) -} - -func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) { - ko, err := bechKeyOut(keyInfo) - if err != nil { - panic(err) - } - - switch viper.Get(cli.OutputFlag) { - case OutputFormatText: - printKeyTextHeader() - printKeyOutput(ko) - - case OutputFormatJSON: - var out []byte - var err error - if viper.GetBool(client.FlagIndentResponse) { - out, err = cdc.MarshalJSONIndent(ko, "", " ") - } else { - out, err = cdc.MarshalJSON(ko) - } - if err != nil { - panic(err) - } - - fmt.Println(string(out)) - } -} - -func printInfos(infos []keys.Info) { - kos, err := keys.Bech32KeysOutput(infos) - if err != nil { - panic(err) - } - - switch viper.Get(cli.OutputFlag) { - case OutputFormatText: - printKeyTextHeader() - for _, ko := range kos { - printKeyOutput(ko) - } - - case OutputFormatJSON: - var out []byte - var err error - - if viper.GetBool(client.FlagIndentResponse) { - out, err = cdc.MarshalJSONIndent(kos, "", " ") - } else { - out, err = cdc.MarshalJSON(kos) - } - - if err != nil { - panic(err) - } - fmt.Println(string(out)) - } -} - -func printKeyOutput(ko keys.KeyOutput) { - fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey) -} - -func printMultiSigKeyOutput(ko keys.KeyOutput) { - for _, pk := range ko.PubKeys { - fmt.Printf("%d\t%d\t\t%s\t%s\n", pk.Weight, ko.Threshold, pk.Address, pk.PubKey) - } -} - -func printKeyAddress(info keys.Info, bechKeyOut bechKeyOutFn) { - ko, err := bechKeyOut(info) - if err != nil { - panic(err) - } - - fmt.Println(ko.Address) -} - -func printPubKey(info keys.Info, bechKeyOut bechKeyOutFn) { - ko, err := bechKeyOut(info) - if err != nil { - panic(err) - } - - fmt.Println(ko.PubKey) -} diff --git a/client/lcd/root.go b/client/lcd/root.go new file mode 100644 index 000000000..dc221c3b3 --- /dev/null +++ b/client/lcd/root.go @@ -0,0 +1,111 @@ +package lcd + +import ( + "fmt" + "net" + "net/http" + "os" + "time" + + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/log" + rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + keybase "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + + // unnamed import of statik for swagger UI support + _ "github.com/terra-project/core/client/lcd/statik" +) + +// RestServer represents the Light Client Rest server +type RestServer struct { + Mux *mux.Router + CliCtx context.CLIContext + KeyBase keybase.Keybase + + log log.Logger + listener net.Listener +} + +// NewRestServer creates a new rest server instance +func NewRestServer(cdc *codec.Codec) *RestServer { + r := mux.NewRouter() + cliCtx := context.NewCLIContext().WithCodec(cdc) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") + + return &RestServer{ + Mux: r, + CliCtx: cliCtx, + log: logger, + } +} + +// Start starts the rest server +func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTimeout uint) (err error) { + server.TrapSignal(func() { + err := rs.listener.Close() + rs.log.Error("error closing listener", "err", err) + }) + + cfg := rpcserver.DefaultConfig() + cfg.MaxOpenConnections = maxOpen + cfg.ReadTimeout = time.Duration(readTimeout) * time.Second + cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second + + rs.listener, err = rpcserver.Listen(listenAddr, cfg) + if err != nil { + return + } + rs.log.Info( + fmt.Sprintf( + "Starting application REST service (chain-id: %q)...", + viper.GetString(flags.FlagChainID), + ), + ) + + return rpcserver.StartHTTPServer(rs.listener, rs.Mux, rs.log, cfg) +} + +// ServeCommand will start the application REST service as a blocking process. It +// takes a codec to create a RestServer object and a function to register all +// necessary routes. +func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.Command { + cmd := &cobra.Command{ + Use: "rest-server", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: func(cmd *cobra.Command, args []string) (err error) { + rs := NewRestServer(cdc) + + registerRoutesFn(rs) + rs.registerSwaggerUI() + + // Start the rest server and return error if one exists + err = rs.Start( + viper.GetString(flags.FlagListenAddr), + viper.GetInt(flags.FlagMaxOpenConnections), + uint(viper.GetInt(flags.FlagRPCReadTimeout)), + uint(viper.GetInt(flags.FlagRPCWriteTimeout)), + ) + + return err + }, + } + + return flags.RegisterRestServerFlags(cmd) +} + +func (rs *RestServer) registerSwaggerUI() { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + staticServer := http.FileServer(statikFS) + rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) +} diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 446e3fe61..fe12ad054 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -5,9 +5,13 @@ info: title: 'Terra-Lite for Terra' description: A REST interface for state queries, transaction generation and broadcasting. tags: - - name: Tendermint + - name: Transactions + description: Search, encode, or broadcast transactions. + - name: Tendermint RPC description: Tendermint APIs, such as query blocks, transactions and validatorset - - name: Transaction + - name: Auth + description: Authenticate accounts + - name: Bank description: Create and broadcast transactions - name: Staking description: Stake module APIs @@ -15,8 +19,8 @@ tags: description: Slashing module APIs - name: Distribution description: Fee distribution module APIs - - name: Budget - description: Budget program modules APIs + - name: Supply + description: Supply module APIs - name: Market description: Market modules APIs - name: Oracle @@ -24,6 +28,7 @@ tags: - name: Treasury description: Treasury modules APIs - name: version + - name: Misc description: Query app version schemes: - https @@ -32,32 +37,12 @@ securityDefinitions: kms: type: basic paths: - /version: - get: - summary: Version of Terra-lite - tags: - - version - description: Get the version of terra-lite running locally to compare against expected - responses: - 200: - description: Plaintext version i.e. "v0.25.0" - /node_version: - get: - summary: Version of the connected node - tags: - - version - description: Get the version of the SDK running on the connected node to compare against expected - responses: - 200: - description: Plaintext version i.e. "v0.25.0" - 500: - description: failed to query node version /node_info: get: description: Information about the connected node summary: The properties of the connected node tags: - - Tendermint + - Tendermint RPC produces: - application/json responses: @@ -66,62 +51,87 @@ paths: schema: type: object properties: - id: - type: string - moniker: - type: string - example: validator-name - protocol_version: + application_version: properties: - p2p: + build_tags: type: string - example: 7 - block: + client_name: type: string - example: 10 - app: + commit: type: string - example: 0 - network: - type: string - example: ricewine-0001 - channels: - type: string - listen_addr: - type: string - example: 192.168.56.1:26656 - version: - description: Tendermint version - type: string - example: 0.15.0 - other: - description: more information on versions - type: object + go: + type: string + name: + type: string + server_name: + type: string + version: + type: string + node_info: properties: - tx_index: + id: + type: string + moniker: type: string - example: on - rpc_address: + example: validator-name + protocol_version: + properties: + p2p: + type: string + example: 7 + block: + type: string + example: 10 + app: + type: string + example: 0 + network: type: string - example: tcp://0.0.0.0:26657 + example: gaia-2 + channels: + type: string + listen_addr: + type: string + example: 192.168.56.1:26656 + version: + description: Tendermint version + type: string + example: 0.15.0 + other: + description: more information on versions + type: object + properties: + tx_index: + type: string + example: on + rpc_address: + type: string + example: tcp://0.0.0.0:26657 500: description: Failed to query node status /syncing: get: summary: Syncing state of node tags: - - Tendermint - description: Get if the node is currently syncing with other nodes + - Tendermint RPC + description: Get if the node is currently syning with other nodes + produces: + - application/json responses: 200: - description: '"true" or "false"' + description: Node syncing status + schema: + type: object + properties: + syncing: + type: boolean 500: description: Server internal error /blocks/latest: get: summary: Get the latest block tags: - - Tendermint + - Tendermint RPC produces: - application/json responses: @@ -135,7 +145,7 @@ paths: get: summary: Get a block at a certain height tags: - - Tendermint + - Tendermint RPC produces: - application/json parameters: @@ -160,7 +170,7 @@ paths: get: summary: Get the latest validator set tags: - - Tendermint + - Tendermint RPC produces: - application/json responses: @@ -170,7 +180,7 @@ paths: type: object properties: block_height: - type: number + type: string validators: type: array items: @@ -181,7 +191,7 @@ paths: get: summary: Get a validator set a certain height tags: - - Tendermint + - Tendermint RPC produces: - application/json parameters: @@ -198,7 +208,7 @@ paths: type: object properties: block_height: - type: number + type: string validators: type: array items: @@ -213,7 +223,8 @@ paths: get: summary: Get a Tx by hash tags: - - Tendermint + - Transactions + description: Retrieve a transaction using its hash. produces: - application/json parameters: @@ -222,7 +233,7 @@ paths: description: Tx hash required: true type: string - x-example: 88D6B85EAB87D43CDF50F39C22FC2237A37FEDC4CE723200AD0AF48CBEDBC317 + x-example: BCBE20E8D46758B96AE5883B792858296AC06E51435490FBDCAE25A72B3CC76B responses: 200: description: Tx with the provided hash @@ -233,18 +244,22 @@ paths: /txs: get: tags: - - Tendermint + - Transactions summary: Search transactions - description: Search transactions by tag(s). + description: Search transactions by events. produces: - application/json parameters: - in: query - name: tag + name: message.action type: string - description: "transaction tags such as 'action=submit-proposal' and 'sender=terra1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe2fet0' which results in the following endpoint: 'GET /txs?action=submit-proposal&sender=terra1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe2fet0'" - required: true - x-example: 'TODO' + description: "transaction events such as 'message.action=send' which results in the following endpoint: 'GET /txs?message.action=send'" + x-example: "send" + - in: query + name: message.sender + type: string + description: "transaction tags with sender: 'GET /txs?message.action=send&message.sender=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv'" + x-example: "terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv" - in: query name: page description: Page number @@ -257,16 +272,16 @@ paths: x-example: 1 responses: 200: - description: All txs matching the provided tags + description: All txs matching the provided events schema: $ref: "#/definitions/PaginatedQueryTxs" 400: - description: Invalid search tags + description: Invalid search events 500: description: Internal Server Error post: tags: - - Tendermint + - Transactions summary: Broadcast a signed tx description: Broadcast a signed tx to a full node consumes: @@ -296,7 +311,7 @@ paths: /txs/encode: post: tags: - - Transaction + - Transactions summary: Encode a transaction to the Amino wire format description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes consumes: @@ -325,12 +340,50 @@ paths: 400: description: The tx was malformated 500: - description: Server internal error + description: Server internal error + /txs/estimate_fee: + post: + tags: + - Transactions + summary: Estimate fees and gas of a transaction + description: Estimate fees and gas of a transaction according to given parameters + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: estimate_req + description: The tx and gas params to compute fees + required: true + schema: + type: object + properties: + tx: + $ref: "#/definitions/StdTx" + gas_adjustment: + type: string + description: only used when tx's gas is "0", default "1" + example: "1.4" + gas_prices: + description: used to compute gas fees = (gas * gas_prices), default 0 + type: array + items: + $ref: "#/definitions/DecCoin" + responses: + 200: + description: Estimated gas and fees object + schema: + $ref: "#/definitions/EstimateFeeResp" + 400: + description: The tx was malformated + 500: + description: Server internal error /bank/balances/{address}: get: summary: Get the account balances tags: - - Transaction + - Bank produces: - application/json parameters: @@ -339,7 +392,7 @@ paths: description: Account address in bech32 format required: true type: string - x-example: terra16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv responses: 200: description: Account balances @@ -347,15 +400,13 @@ paths: type: array items: $ref: "#/definitions/Coin" - 204: - description: There is no data for the requested account 500: description: Server internal error /bank/accounts/{address}/transfers: post: summary: Send coins from one account to another tags: - - Transaction + - Bank consumes: - application/json produces: @@ -366,7 +417,7 @@ paths: description: Account address in bech32 format required: true type: string - x-example: terra16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: body name: account description: The sender and tx information @@ -393,7 +444,7 @@ paths: get: summary: Get the account information on blockchain tags: - - Keys + - Auth produces: - application/json parameters: @@ -402,21 +453,17 @@ paths: description: Account address required: true type: string - x-example: terra16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv responses: 200: - description: Account information on the blockchain (one of Account and GradedVestingAccount and LazyGradedVestingAccount) + description: Account information on the blockchain (one of Account and LazyGradedVestingAccount) schema: type: object properties: Account: $ref: "#/definitions/Account" - GradedVestingAccount: - $ref: "#/definitions/GradedVestingAccount" LazyGradedVestingAccount: $ref: "#/definitions/LazyGradedVestingAccount" - - 204: description: No content about this account address 500: @@ -425,7 +472,7 @@ paths: post: summary: "Generate multisig signatures for transactions generated offline" tags: - - Keys + - Auth produces: - application/json parameters: @@ -434,10 +481,10 @@ paths: description: Account address required: true type: string - x-example: terra16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: body name: multisig_req - description: multisign request information + description: multisign request information; pubkey is optional param in case multisig account never used before required: true schema: $ref: "#/definitions/MultiSignReq" @@ -460,7 +507,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv get: summary: Get all delegations from a delegator tags: @@ -493,7 +540,7 @@ paths: $ref: "#/definitions/Address" validator_address: $ref: "#/definitions/ValidatorAddress" - amount: + delegation: $ref: "#/definitions/Coin" tags: - Staking @@ -519,13 +566,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Query the current delegation between a delegator and a validator tags: @@ -548,7 +595,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv get: summary: Get all unbonding delegations from a delegator tags: @@ -581,8 +628,9 @@ paths: $ref: "#/definitions/Address" validator_address: $ref: "#/definitions/ValidatorAddress" - amount: - $ref: "#/definitions/Coin" + shares: + type: string + example: "100" tags: - Staking consumes: @@ -607,13 +655,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Query all unbonding delegations between a delegator and a validator tags: @@ -624,9 +672,7 @@ paths: 200: description: OK schema: - type: array - items: - $ref: "#/definitions/UnbondingDelegation" + $ref: "#/definitions/UnbondingDelegationPair" 400: description: Invalid delegator address or validator address 500: @@ -670,7 +716,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv post: summary: Submit a redelegation parameters: @@ -688,8 +734,9 @@ paths: $ref: "#/definitions/ValidatorAddress" validator_dst_address: $ref: "#/definitions/ValidatorAddress" - amount: - $ref: "#/definitions/Coin" + shares: + type: string + example: "100" tags: - Staking consumes: @@ -712,7 +759,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv get: summary: Query all validators that a delegator is bonded to tags: @@ -737,13 +784,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: path name: validatorAddr description: Bech32 ValAddress of Delegator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Query a validator that a delegator is bonded to tags: @@ -918,7 +965,7 @@ paths: type: string 500: description: Internal Server Error - /staking/params: + /staking/parameters: get: summary: Get the current staking parameter values tags: @@ -1001,8 +1048,6 @@ paths: type: array items: $ref: "#/definitions/SigningInfo" - 204: - description: No validators with sign info 400: description: Invalid validator public key for one of the validators 500: @@ -1056,19 +1101,19 @@ paths: type: object properties: max_evidence_age: - type: integer + type: string signed_blocks_window: - type: integer + type: string min_signed_per_window: - type: integer + type: string double_sign_unbond_duration: - type: integer + type: string downtime_unbond_duration: - type: integer + type: string slash_fraction_double_sign: - type: integer + type: string slash_fraction_downtime: - type: integer + type: string 500: description: Internal Server Error /distribution/delegators/{delegatorAddr}/rewards: @@ -1078,7 +1123,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv get: summary: Get the total rewards balance from all delegations description: Get the sum of all the rewards earned by delegations by a single delegator @@ -1129,13 +1174,13 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv - in: path name: validatorAddr description: Bech32 OperatorAddress of validator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Query a delegation reward description: Query a single delegation reward by a delegator @@ -1188,7 +1233,7 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string - x-example: terra167w96tdvmazakdwkw2u57227eduula2cy572lf + x-example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv get: summary: Get the rewards withdrawal address description: Get the delegations' rewards withdrawal address. This is the address in which the user will receive the reward funds @@ -1241,7 +1286,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Validator distribution information description: Query the distribution information of a single validator @@ -1265,7 +1310,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string - x-example: terravaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Fee distribution outstanding rewards of a single validator tags: @@ -1288,7 +1333,7 @@ paths: description: Bech32 OperatorAddress of validator required: true type: string - x-example: terra1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe2fet0 + x-example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l get: summary: Commission and self-delegation rewards of a single validator description: Query the commission and self-delegation rewards of validator. @@ -1370,210 +1415,43 @@ paths: type: string 500: description: Internal Server Error - /budget/programs/submit: - post: - summary: Posting new budget program - tags: - - Budget - produces: - - application/json - parameters: - - in: body - name: Post Program request body - schema: - $ref: "#/definitions/submitProgramReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/StdTx" - 400: - description: Bad Request - 500: - description: Internal Server Error - /budget/programs/{programId}/withdraw: - post: - summary: Send withdraw request - tags: - - Budget - produces: - - application/json - parameters: - - in: path - name: programId - description: Program ID - required: true - type: integer - - in: body - name: Post Withdraw request body - schema: - $ref: "#/definitions/withdrawReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/StdTx" - 400: - description: Bad Request - 500: - description: Internal Server Error - /budget/programs/{programId}/votes: - parameters: - - in: path - name: programId - description: Program ID - required: true - type: integer - post: - summary: Vote to the program by programId - tags: - - Budget - produces: - - application/json - parameters: - - in: body - name: Budget Vote request body - schema: - $ref: "#/definitions/budgetVoteReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/StdTx" - 400: - description: Bad Request - 500: - description: Internal Server Error + /supply/total: get: - summary: Get the budget program voting records of a denom + summary: Total supply of coins in the chain tags: - - Budget + - Supply produces: - application/json responses: 200: description: OK schema: - type: object - properties: - votes: - type: array - items: - $ref: "#/definitions/MsgVoteProgram" - 400: - description: Bad Request + $ref: "#/definitions/Supply" 500: description: Internal Server Error - /budget/programs/{programID}/votes/{voter}: + /supply/total/{denomination}: parameters: - in: path - name: programID - description: Program ID to get - required: true - type: integer - - in: path - name: voter - description: Bech32 OperatorAddress of voter + name: denomination + description: Coin denomination required: true type: string + x-example: uatom get: - summary: Get the budget program voting record of RestVoter - tags: - - Budget - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - votes: - type: array - items: - $ref: "#/definitions/MsgVoteProgram" - 400: - description: Bad Request - 500: - description: Internal Server Error - /budget/programs/actives: - get: - summary: Get active budget programs - tags: - - Budget - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - actives: - type: array - items: - $ref: "#/definitions/Program" - 400: - description: Bad Request - 500: - description: Internal Server Error - /budget/programs/candidates: - get: - summary: Get candidate budget programs - tags: - - Budget - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - candidates: - type: array - items: - $ref: "#/definitions/Program" - 400: - description: Bad Request - 500: - description: Internal Server Error - /budget/programs/{programId}: - get: - summary: Get a budget program by program-id + summary: Total supply of a single coin denomination tags: - - Budget + - Supply produces: - application/json - parameters: - - in: path - name: programId - description: Program ID - required: true - type: integer responses: 200: description: OK schema: - $ref: "#/definitions/Program" + type: string 400: - description: Bad Request + description: Invalid coin denomination 500: description: Internal Server Error - /budget/params: - get: - summary: Get budget params - tags: - - Budget - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BudgetParams" - 404: - description: Not Found /market/swap: post: summary: Swap coin with another coin @@ -1619,7 +1497,25 @@ paths: $ref: "#/definitions/Coin" 500: description: Internal Server Error - /market/params: + /market/prev_day_issuance: + get: + summary: Get prev day issuance + tags: + - Market + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Coin" + 400: + description: Bad Request + 500: + description: Internal Server Error + /market/parameters: get: summary: Get market params tags: @@ -1677,12 +1573,9 @@ paths: 200: description: OK schema: - type: object - properties: - votes: - type: array - items: - $ref: "#/definitions/PriceVote" + type: array + items: + $ref: "#/definitions/PriceVote" 400: description: Bad Request 500: @@ -1708,12 +1601,9 @@ paths: 200: description: OK schema: - type: object - properties: - votes: - type: array - items: - $ref: "#/definitions/PriceVote" + type: array + items: + $ref: "#/definitions/PriceVote" 400: description: Bad Request 500: @@ -1760,12 +1650,9 @@ paths: 200: description: OK schema: - type: object - properties: - prevotes: - type: array - items: - $ref: "#/definitions/PricePrevote" + type: array + items: + $ref: "#/definitions/PricePrevote" 400: description: Bad Request 500: @@ -1791,12 +1678,9 @@ paths: 200: description: OK schema: - type: object - properties: - prevotes: - type: array - items: - $ref: "#/definitions/PricePrevote" + type: array + items: + $ref: "#/definitions/PricePrevote" 400: description: Bad Request 500: @@ -1818,11 +1702,8 @@ paths: 200: description: current price of denom i.e. "1000.0" schema: - type: object - properties: - price: - type: number - example: "1872.000000000000000000" + type: number + example: "1872.000000000000000000" 400: description: Bad Request 500: @@ -1838,18 +1719,67 @@ paths: 200: description: OK schema: - type: object - properties: - actives: - type: array - items: - type: string - example: ["uluna", "usdr"] + type: array + items: + type: string + example: ["uluna", "usdr"] + 400: + description: Bad Request + 500: + description: Internal Server Error + /oracle/voters/{voter}/voting_info: + get: + summary: Get voting info of a voter + tags: + - Oracle + produces: + - application/json + parameters: + - in: path + name: voter + required: true + type: string + responses: + 200: + description: OK + schema: + $ref: "#/definitions/VotingInfo" + 400: + description: Bad Request + 500: + description: Internal Server Error + /oracle/voting_infos: + get: + summary: Get voting infos + tags: + - Oracle + produces: + - application/json + parameters: + - in: query + name: page + description: Page number + type: integer + required: true + x-example: 1 + - in: query + name: limit + description: Maximum number of items per page + type: integer + required: true + x-example: 5 + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/VotingInfo" 400: description: Bad Request 500: description: Internal Server Error - /oracle/params: + /oracle/parameters: get: summary: Get oracle params tags: @@ -1865,7 +1795,7 @@ paths: description: Bad Request 500: description: Internal Server Error - /treasury/tax-rate: + /treasury/tax_rate: get: summary: Get current tax rate tags: @@ -1876,15 +1806,12 @@ paths: 200: description: OK schema: - type: object - properties: - tax_rate: - type: number - format: float - example: "0.05" + type: number + format: float + example: "0.05" 500: description: Internal Server Error - /treasury/tax-rate/{epoch}: + /treasury/tax_rate/{epoch}: get: summary: Get tax rate at epoch tags: @@ -1901,15 +1828,12 @@ paths: 200: description: OK schema: - type: object - properties: - tax_rate: - type: number - format: float - example: "0.05" + type: number + format: float + example: "0.05" 500: description: Internal Server Error - /treasury/tax-cap/{denom}: + /treasury/tax_cap/{denom}: get: summary: Get tax cap of the denom tags: @@ -1926,39 +1850,28 @@ paths: 200: description: OK schema: - type: object - properties: - tax_cap: - type: integer - example: "100000" + type: integer + example: "100000" 404: description: Not Found - /treasury/reward-weight: + /treasury/reward_weight: get: summary: Get current reward weight tags: - Treasury produces: - application/json - parameters: - - in: path - name: epoch - description: Epoch number - required: true - type: integer responses: 200: description: OK schema: - type: object - properties: - reward_weight: - type: number - example: "5" - description: 5% + type: number + format: float + example: "0.05" + description: 5% 500: description: Internal Server Error - /treasury/reward-weight/{denom}: + /treasury/reward_weight/{epoch}: get: summary: Get reward weight at epoch tags: @@ -1967,76 +1880,43 @@ paths: - application/json parameters: - in: path - name: denom - description: Denom - required: true - type: string - responses: - 200: - description: OK - schema: - type: object - properties: - reward_weight: - type: number - example: "5" - description: 5% - 500: - description: Internal Server Error - /treasury/issuance/{denom}: - get: - summary: Get total issuance count of denom at current height - tags: - - Treasury - produces: - - application/json - parameters: - - in: path - name: denom - description: Denom to get + name: epoch + description: Epoch number required: true type: string responses: 200: description: OK schema: - type: object - properties: - issuance: - type: integer - example: "500" + type: number + format: float + example: "0.05" + description: 5% 500: description: Internal Server Error - /treasury/issuance/{denom}/{day}: + /treasury/historical_issuance/{epoch}: get: - summary: Get total issuance count of denom at specific day + summary: Get total issuance at the epoch tags: - Treasury produces: - application/json parameters: - in: path - name: denom - description: Denom to get - required: true - type: string - - in: path - name: day - description: the \# of date after genesis time, a user want to query + name: epoch + description: Epoch number required: true - type: string + type: integer responses: 200: description: OK schema: - type: object - properties: - issuance: - type: integer - example: "500" + type: array + items: + $ref: "#/definitions/Coin" 500: description: Internal Server Error - /treasury/tax-proceeds: + /treasury/tax_proceeds: get: summary: Get current tax proceeds tags: @@ -2047,15 +1927,12 @@ paths: 200: description: OK schema: - type: object - properties: - tax_proceeds: - type: array - items: - $ref: "#/definitions/Coin" + type: array + items: + $ref: "#/definitions/Coin" 500: description: Internal Server Error - /treasury/tax-proceeds/{epoch}: + /treasury/tax_proceeds/{epoch}: get: summary: Get tax proceeds at epoch tags: @@ -2072,15 +1949,12 @@ paths: 200: description: OK schema: - type: object - properties: - tax_proceeds: - type: array - items: - $ref: "#/definitions/Coin" + type: array + items: + $ref: "#/definitions/Coin" 500: description: Internal Server Error - /treasury/seigniorage-proceeds: + /treasury/seigniorage_proceeds: get: summary: retrieves the size of the seigniorage pool tags: @@ -2091,14 +1965,11 @@ paths: 200: description: OK schema: - type: object - properties: - seigniorage_proceeds: - type: integer - example: "0" + type: integer + example: "0" 500: description: Internal Server - /treasury/seigniorage-proceeds/{epoch}: + /treasury/seigniorage_proceeds/{epoch}: get: summary: retrieves the size of the seigniorage pool at epoch tags: @@ -2115,14 +1986,11 @@ paths: 200: description: OK schema: - type: object - properties: - seigniorage_proceeds: - type: integer - example: "0" + type: integer + example: "0" 500: description: Internal Server Error - /treasury/current-epoch: + /treasury/current_epoch: get: summary: Get current epoch tags: @@ -2133,14 +2001,11 @@ paths: 200: description: OK schema: - type: object - properties: - current_epoch: - type: number - example: "724" + type: number + example: "724" 404: description: Not Found - /treasury/params: + /treasury/parameters: get: summary: Get treasury module params tags: @@ -2236,20 +2101,29 @@ definitions: Address: type: string description: bech32 encoded address - example: terra1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe2fet0 + example: terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv ValidatorAddress: type: string description: bech32 encoded address - example: terravaloper1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe99ymu + example: terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l Coin: type: object properties: denom: type: string - example: luna + example: uluna amount: type: string example: "50" + DecCoin: + type: object + properties: + denom: + type: string + example: uluna + amount: + type: string + example: "50.000" Hash: type: string example: EE5F3404034C524501629B56E0DDC38FAD651F04 @@ -2301,21 +2175,6 @@ definitions: type: array items: $ref: "#/definitions/TxQuery" - StdSignature: - type: object - properties: - signature: - type: string - example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= - pub_key: - type: object - properties: - type: - type: string - example: "tendermint/PubKeySecp256k1" - value: - type: string - example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH" StdTx: type: object properties: @@ -2334,10 +2193,27 @@ definitions: $ref: "#/definitions/Coin" memo: type: string - signatures: - type: array - items: - $ref: "#/definitions/StdSignature" + signature: + type: object + properties: + signature: + type: string + example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: "tendermint/PubKeySecp256k1" + value: + type: string + example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH" + account_number: + type: string + example: "0" + sequence: + type: string + example: "0" UnsignedStdTx: type: object properties: @@ -2377,7 +2253,7 @@ definitions: properties: chain_id: type: string - example: ricewine-0001 + example: columbus-3 height: type: number example: 1 @@ -2501,14 +2377,14 @@ definitions: properties: from: type: string - example: "terra1ca8etp65w8ewl4zh0gfjxtg3txknu6wqe2fet0" + example: "terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv" description: Sender address or Keybase name to generate a transaction memo: type: string - example: "Sent via Terra Voyager 🚀" + example: "Sent via Terra Station 🚀" chain_id: type: string - example: "ricewine-0001" + example: "columbus-3" account_number: type: string example: "0" @@ -2529,144 +2405,85 @@ definitions: type: boolean example: false description: Estimate gas for a transaction (cannot be used in conjunction with generate_only) - - BaseAccount: + TendermintValidator: type: object properties: - account_number: - type: string address: + $ref: "#/definitions/ValidatorAddress" + pub_key: type: string - coins: - type: array - items: - $ref: "#/definitions/Coin" - public_key: + example: terravalconspub1zcjduepq7mft6gfls57a0a42d7uhx656cckhfvtrlmw744jv4q0mvlv0dypskehfk8 + voting_power: type: string - sequence: + example: "1000" + proposer_priority: type: string - - Account: + example: "1000" + TextProposal: type: object properties: - type: + proposal_id: + type: integer + title: type: string - example: "auth/Account" - value: - $ref: "#/definitions/BaseAccount" - - BaseVestingAccount: - type: object - properties: - BaseAccount: - $ref: "#/definitions/BaseAccount" - original_vesting: - type: array - items: - $ref: "#/definitions/Coin" - delegated_free: - type: array - items: - $ref: "#/definitions/Coin" - delegated_vesting: + description: + type: string + proposal_type: + type: string + proposal_status: + type: string + final_tally_result: + $ref: "#/definitions/TallyResult" + submit_time: + type: string + total_deposit: type: array items: $ref: "#/definitions/Coin" - end_time: + voting_start_time: type: string - example: "0" - - Schedule: + Proposer: type: object properties: - cliff: + proposal_id: type: string - example: "1556085600" - ratio: - type: string - example: "0.100000000000000000" - VestingSchedule: - type: object - properties: - denom: + proposer: type: string - example: "usdr" - schedules: - type: array - items: - $ref: "#/definitions/Schedule" - BaseGradedVestingAccount: + Deposit: type: object properties: - BaseVestingAccount: - $ref: "#/definitions/BaseVestingAccount" - vesting_schedules: + amount: type: array items: - $ref: "#/definitions/VestingSchedule" - GradedVestingAccount: + $ref: "#/definitions/Coin" + proposal_id: + type: string + depositor: + $ref: "#/definitions/Address" + TallyResult: type: object properties: - type: + yes: type: string - example: "core/GradedVestingAccount" - value: - $ref: "#/definitions/BaseGradedVestingAccount" - - LazySchedule: + example: "0.0000000000" + abstain: + type: string + example: "0.0000000000" + no: + type: string + example: "0.0000000000" + no_with_veto: + type: string + example: "0.0000000000" + Vote: type: object properties: - start_time: - type: string - example: "1556085600" - end_time: - type: string - example: "1556085600" - ratio: - type: string - example: "0.100000000000000000" - LazyVestingSchedule: - type: object - properties: - denom: - type: string - example: "usdr" - lazy_schedules: - type: array - items: - $ref: "#/definitions/LazySchedule" - BaseLazyGradedVestingAccount: - type: object - properties: - BaseVestingAccount: - $ref: "#/definitions/BaseVestingAccount" - lazy_vesting_schedules: - type: array - items: - $ref: "#/definitions/LazyVestingSchedule" - LazyGradedVestingAccount: - type: object - properties: - type: - type: string - example: "core/LazyGradedVestingAccount" - value: - $ref: "#/definitions/BaseLazyGradedVestingAccount" - - TendermintValidator: - type: object - properties: - address: - $ref: "#/definitions/ValidatorAddress" - pub_key: + voter: type: string - example: terravalconspub1zcjduepq5zuq4cp0cpfyx6rca6ygz38t446wp3hlp5lepyle4e6e00twmetqsa0vg4 - voting_power: + proposal_id: type: string - example: "1000" - proposer_priority: + option: type: string - example: "1000" Validator: type: object properties: @@ -2674,7 +2491,7 @@ definitions: $ref: "#/definitions/ValidatorAddress" consensus_pubkey: type: string - example: terravalconspub1zcjduepq5zuq4cp0cpfyx6rca6ygz38t446wp3hlp5lepyle4e6e00twmetqsa0vg4 + example: terravalconspub1zcjduepq7mft6gfls57a0a42d7uhx656cckhfvtrlmw744jv4q0mvlv0dypskehfk8 jailed: type: boolean status: @@ -2699,7 +2516,7 @@ definitions: example: "0" bond_intra_tx_counter: type: integer - example: "0" + example: 0 unbonding_height: type: string example: "0" @@ -2732,6 +2549,28 @@ definitions: type: string height: type: integer + UnbondingDelegationPair: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + entries: + type: array + items: + $ref: "#/definitions/UnbondingEntries" + UnbondingEntries: + type: object + properties: + initial_balance: + type: string + balance: + type: string + creation_height: + type: string + min_time: + type: string UnbondingDelegation: type: object properties: @@ -2756,16 +2595,21 @@ definitions: type: string validator_dst_address: type: string + entries: + type: array + items: + $ref: "#/definitions/Redelegation" + RedelegationEntry: + type: object + properties: creation_height: type: integer - min_time: + completion_time: type: integer initial_balance: type: string balance: type: string - shares_src: - type: string shares_dst: type: string ValidatorDistInfo: @@ -2781,6 +2625,13 @@ definitions: type: array items: $ref: "#/definitions/Coin" + PublicKey: + type: object + properties: + type: + type: string + value: + type: string SigningInfo: type: object properties: @@ -2792,6 +2643,109 @@ definitions: type: string missed_blocks_counter: type: string + ParamChange: + type: object + properties: + subspace: + type: string + example: "staking" + key: + type: string + example: "MaxValidators" + subkey: + type: string + example: "" + value: + type: object + Supply: + type: object + properties: + total: + type: array + items: + $ref: "#/definitions/Coin" + BaseAccount: + type: object + properties: + account_number: + type: string + address: + type: string + coins: + type: array + items: + $ref: "#/definitions/Coin" + public_key: + type: string + sequence: + type: string + Account: + type: object + properties: + type: + type: string + example: "auth/Account" + value: + $ref: "#/definitions/BaseAccount" + BaseVestingAccount: + type: object + properties: + BaseAccount: + $ref: "#/definitions/BaseAccount" + original_vesting: + type: array + items: + $ref: "#/definitions/Coin" + delegated_free: + type: array + items: + $ref: "#/definitions/Coin" + delegated_vesting: + type: array + items: + $ref: "#/definitions/Coin" + end_time: + type: string + example: "0" + Schedule: + type: object + properties: + start_time: + type: string + example: "1556085600" + end_time: + type: string + example: "1556085600" + ratio: + type: string + example: "0.100000000000000000" + VestingSchedule: + type: object + properties: + denom: + type: string + example: "usdr" + lazy_schedules: + type: array + items: + $ref: "#/definitions/Schedule" + BaseLazyGradedVestingAccount: + type: object + properties: + BaseVestingAccount: + $ref: "#/definitions/BaseVestingAccount" + vesting_schedules: + type: array + items: + $ref: "#/definitions/VestingSchedule" + LazyGradedVestingAccount: + type: object + properties: + type: + type: string + example: "core/LazyGradedVestingAccount" + value: + $ref: "#/definitions/BaseLazyGradedVestingAccount" SwapReq: type: object properties: @@ -2801,7 +2755,19 @@ definitions: $ref: "#/definitions/Coin" ask_denom: type: string - example: luna + example: uluna + MarketParams: + type: object + properties: + daily_luna_delta_limit: + type: number + example: "0.005" + min_swap_spread: + type: number + example: "0.02" + max_swap_spread: + type: number + example: "0.1" PrevoteReq: type: object properties: @@ -2836,13 +2802,6 @@ definitions: description: "proof salt was used to make prevote hash; initial prevote does not require this field" validator: $ref: "#/definitions/ValidatorAddress" - budgetVoteReq: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - option: - type: boolean PriceVote: type: object properties: @@ -2868,74 +2827,17 @@ definitions: submit_block: type: number example: "1" - submitProgramReq: + VotingInfo: type: object properties: - base_req: - $ref: "#/definitions/BaseReq" - title: - type: string - example: "title" - description: + address: + $ref: "#/definitions/ValidatorAddress" + start_height: type: string - example: "description" - executor: - $ref: "#/definitions/Address" - withdrawReq: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - Program: - type: object - properties: - program_id: - type: number - title: + index_offset: type: string - description: + missed_votes_counter: type: string - submitter: - $ref: "#/definitions/Address" - executor: - $ref: "#/definitions/Address" - submit_time: - type: number - MsgVoteProgram: - type: object - properties: - program_id: - type: number - option: - type: boolean - voter: - $ref: "#/definitions/Address" - BudgetParams: - type: object - properties: - active_threshold: - type: number - example: "0.1" - legacy_threshold: - type: number - example: "0.00" - vote_period: - type: number - example: "1000000" - deposit: - $ref: "#/definitions/Coin" - MarketParams: - type: object - properties: - daily_luna_delta_limit: - type: number - example: "0.005" - min_swap_spread: - type: number - example: "0.02" - max_swap_spread: - type: number - example: "0.1" OracleParams: type: object properties: @@ -2951,17 +2853,6 @@ definitions: oracle_reward_band: type: number example: "0.02" - Claim: - type: object - properties: - class: - type: string - example: ["oracle", "budget"] - weight: - type: integer - example: "100" - recipient: - $ref: "#/definitions/Address" PolicyConstraints: type: object properties: @@ -3015,6 +2906,17 @@ definitions: type: number format: float example: "0.9" + MultiSignPubKey: + type: object + properties: + threshold: + type: number + example: 1 + pubkeys: + type: array + items: + type: string + example: terrapub1addwnpepq2l6pwj8h9fwxdjuge7lazu0sszpkck0nlhjag6q9drffrd93atywdt8ksu MultiSignReq: type: object properties: @@ -3029,3 +2931,31 @@ definitions: $ref: "#/definitions/StdSignature" signature_only: type: boolean + pubkey: + $ref: "#/definitions/MultiSignPubKey" + StdSignature: + type: object + properties: + signature: + type: string + example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: "tendermint/PubKeySecp256k1" + value: + type: string + example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH" + EstimateFeeResp: + type: object + properties: + fees: + type: array + items: + $ref: "#/definitions/Coin" + gas: + type: number + format: integer + example: 10000 diff --git a/client/tx/query.go b/client/tx/query.go deleted file mode 100644 index 858b7a6fe..000000000 --- a/client/tx/query.go +++ /dev/null @@ -1,50 +0,0 @@ -package tx - -import ( - "net/http" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - - "github.com/terra-project/core/types" -) - -// QueryTxsByTagsRequestHandlerFn implements a REST handler that searches for -// transactions by tags. -func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - tags []string - txs []sdk.TxResponse - page, limit int - ) - - err := r.ParseForm() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, - sdk.AppendMsgToErr("could not parse query parameters", err.Error())) - return - } - - if len(r.Form) == 0 { - rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) - return - } - - tags, page, limit, err = rest.ParseHTTPArgs(r) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txs, totalCount, err := SearchTxs(cliCtx, cdc, tags, page, limit) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, types.NewTxSearchResponse(txs, totalCount), cliCtx.Indent) - } -} diff --git a/client/tx/root.go b/client/tx/root.go deleted file mode 100644 index f1536feca..000000000 --- a/client/tx/root.go +++ /dev/null @@ -1,17 +0,0 @@ -package tx - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - sdktx "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/codec" -) - -// register REST routes -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/txs/{hash}", sdktx.QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/txs", QueryTxsByTagsRequestHandlerFn(cliCtx, cdc)).Methods("GET") - r.HandleFunc("/txs", sdktx.BroadcastTxRequest(cliCtx, cdc)).Methods("POST") - r.HandleFunc("/txs/encode", sdktx.EncodeTxRequestHandlerFn(cdc, cliCtx)).Methods("POST") -} diff --git a/client/tx/utils.go b/client/tx/utils.go deleted file mode 100644 index a7af565ec..000000000 --- a/client/tx/utils.go +++ /dev/null @@ -1,173 +0,0 @@ -package tx - -import ( - "encoding/hex" - "errors" - "strings" - "time" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - ctypes "github.com/tendermint/tendermint/rpc/core/types" -) - -// SearchTxs performs a search for transactions for a given set of tags via -// Tendermint RPC. It returns a slice of Info object containing txs and metadata. -// An error is returned if the query fails. -func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, int, error) { - if len(tags) == 0 { - return nil, 0, errors.New("must declare at least one tag to search") - } - - if page <= 0 { - return nil, 0, errors.New("page must greater than 0") - } - - if limit <= 0 { - return nil, 0, errors.New("limit must greater than 0") - } - - // XXX: implement ANY - query := strings.Join(tags, " AND ") - - node, err := cliCtx.GetNode() - if err != nil { - return nil, 0, err - } - - prove := !cliCtx.TrustNode - - resTxs, err := node.TxSearch(query, prove, page, limit) - if err != nil { - return nil, 0, err - } - - if prove { - for _, tx := range resTxs.Txs { - err := ValidateTxResult(cliCtx, tx) - if err != nil { - return nil, 0, err - } - } - } - - resBlocks, err := getBlocksForTxResults(cliCtx, resTxs.Txs) - if err != nil { - return nil, 0, err - } - - txs, err := formatTxResults(cdc, resTxs.Txs, resBlocks) - if err != nil { - return nil, 0, err - } - - return txs, resTxs.TotalCount, nil -} - -// formatTxResults parses the indexed txs into a slice of TxResponse objects. -func formatTxResults(cdc *codec.Codec, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]sdk.TxResponse, error) { - var err error - out := make([]sdk.TxResponse, len(resTxs)) - for i := range resTxs { - out[i], err = formatTxResult(cdc, resTxs[i], resBlocks[resTxs[i].Height]) - if err != nil { - return nil, err - } - } - - return out, nil -} - -// ValidateTxResult performs transaction verification. -func ValidateTxResult(cliCtx context.CLIContext, resTx *ctypes.ResultTx) error { - if !cliCtx.TrustNode { - check, err := cliCtx.Verify(resTx.Height) - if err != nil { - return err - } - err = resTx.Proof.Validate(check.Header.DataHash) - if err != nil { - return err - } - } - return nil -} - -func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { - node, err := cliCtx.GetNode() - if err != nil { - return nil, err - } - - resBlocks := make(map[int64]*ctypes.ResultBlock) - - for _, resTx := range resTxs { - if _, ok := resBlocks[resTx.Height]; !ok { - resBlock, err := node.Block(&resTx.Height) - if err != nil { - return nil, err - } - - resBlocks[resTx.Height] = resBlock - } - } - - return resBlocks, nil -} - -func formatTxResult(cdc *codec.Codec, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) { - tx, err := parseTx(cdc, resTx.Tx) - if err != nil { - return sdk.TxResponse{}, err - } - - return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil -} - -func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { - var tx auth.StdTx - - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) - if err != nil { - return nil, err - } - - return tx, nil -} - -func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) (sdk.TxResponse, error) { - hash, err := hex.DecodeString(hashHexStr) - if err != nil { - return sdk.TxResponse{}, err - } - - node, err := cliCtx.GetNode() - if err != nil { - return sdk.TxResponse{}, err - } - - resTx, err := node.Tx(hash, !cliCtx.TrustNode) - if err != nil { - return sdk.TxResponse{}, err - } - - if !cliCtx.TrustNode { - if err = ValidateTxResult(cliCtx, resTx); err != nil { - return sdk.TxResponse{}, err - } - } - - resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx}) - if err != nil { - return sdk.TxResponse{}, err - } - - out, err := formatTxResult(cdc, resTx, resBlocks[resTx.Height]) - if err != nil { - return out, err - } - - return out, nil -} diff --git a/cmd/contract_tests/main.go b/cmd/contract_tests/main.go new file mode 100644 index 000000000..740c771f3 --- /dev/null +++ b/cmd/contract_tests/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "github.com/snikch/goodman/hooks" + "github.com/snikch/goodman/transaction" +) + +func main() { + // This must be compiled beforehand and given to dredd as parameter, in the meantime the server should be running + h := hooks.NewHooks() + server := hooks.NewServer(hooks.NewHooksRunner(h)) + h.BeforeAll(func(t []*transaction.Transaction) { + fmt.Println("Sleep 5 seconds before all modification") + }) + h.BeforeEach(func(t *transaction.Transaction) { + fmt.Println("before each modification") + }) + h.Before("/version > GET", func(t *transaction.Transaction) { + fmt.Println("before version TEST") + }) + h.Before("/node_version > GET", func(t *transaction.Transaction) { + fmt.Println("before node_version TEST") + }) + h.BeforeEachValidation(func(t *transaction.Transaction) { + fmt.Println("before each validation modification") + }) + h.BeforeValidation("/node_version > GET", func(t *transaction.Transaction) { + fmt.Println("before validation node_version TEST") + }) + h.After("/node_version > GET", func(t *transaction.Transaction) { + fmt.Println("after node_version TEST") + }) + h.AfterEach(func(t *transaction.Transaction) { + fmt.Println("after each modification") + }) + h.AfterAll(func(t []*transaction.Transaction) { + fmt.Println("after all modification") + }) + server.Serve() + defer server.Listener.Close() + fmt.Print(h) +} diff --git a/cmd/init/collect.go b/cmd/init/collect.go deleted file mode 100644 index a23016dbe..000000000 --- a/cmd/init/collect.go +++ /dev/null @@ -1,146 +0,0 @@ -package init - -// DONTCOVER - -import ( - "encoding/json" - "path/filepath" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/types" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/x/auth" -) - -const ( - flagGenTxDir = "gentx-dir" -) - -type initConfig struct { - ChainID string - GenTxsDir string - Name string - NodeID string - ValPubKey crypto.PubKey -} - -// nolint -func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "collect-gentxs", - Short: "Collect genesis txs and output a genesis.json file", - RunE: func(_ *cobra.Command, _ []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) - name := viper.GetString(client.FlagName) - nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) - if err != nil { - return err - } - - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) - if err != nil { - return err - } - - genTxsDir := viper.GetString(flagGenTxDir) - if genTxsDir == "" { - genTxsDir = filepath.Join(config.RootDir, "config", "gentx") - } - - toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) - initCfg := newInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey) - - appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) - if err != nil { - return err - } - - toPrint.AppMessage = appMessage - - // print out some key information - return displayInfo(cdc, toPrint) - }, - } - - cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") - cmd.Flags().String(flagGenTxDir, "", - "override default \"gentx\" directory from which collect and execute "+ - "genesis transactions; default [--home]/config/gentx/") - return cmd -} - -func genAppStateFromConfig( - cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc, -) (appState json.RawMessage, err error) { - - genFile := config.GenesisFile() - var ( - appGenTxs []auth.StdTx - persistentPeers string - genTxs []json.RawMessage - jsonRawTx json.RawMessage - ) - - // process genesis transactions, else create default genesis.json - appGenTxs, persistentPeers, err = app.CollectStdTxs( - cdc, config.Moniker, initCfg.GenTxsDir, genDoc, - ) - if err != nil { - return - } - - genTxs = make([]json.RawMessage, len(appGenTxs)) - config.P2P.PersistentPeers = persistentPeers - - for i, stdTx := range appGenTxs { - jsonRawTx, err = cdc.MarshalJSON(stdTx) - if err != nil { - return - } - genTxs[i] = jsonRawTx - } - - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - - appState, err = app.TerraAppGenStateJSON(cdc, genDoc, genTxs) - if err != nil { - return - } - - err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) - return -} - -func newInitConfig(chainID, genTxsDir, name, nodeID string, - valPubKey crypto.PubKey) initConfig { - - return initConfig{ - ChainID: chainID, - GenTxsDir: genTxsDir, - Name: name, - NodeID: nodeID, - ValPubKey: valPubKey, - } -} - -func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, - appMessage json.RawMessage) printInfo { - - return printInfo{ - Moniker: moniker, - ChainID: chainID, - NodeID: nodeID, - GenTxsDir: genTxsDir, - AppMessage: appMessage, - } -} diff --git a/cmd/init/genesis_accts.go b/cmd/init/genesis_accts.go deleted file mode 100644 index f5d5b417d..000000000 --- a/cmd/init/genesis_accts.go +++ /dev/null @@ -1,142 +0,0 @@ -package init - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/common" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" -) - -// AddGenesisAccountCmd returns add-genesis-account cobra Command. -func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", - Short: "Add genesis account to genesis.json", - Args: cobra.ExactArgs(2), - RunE: func(_ *cobra.Command, args []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) - - addr, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome)) - if err != nil { - return err - } - - info, err := kb.Get(args[0]) - if err != nil { - return err - } - - addr = info.GetAddress() - } - - coins, err := sdk.ParseCoins(args[1]) - if err != nil { - return err - } - - vestingStart := viper.GetInt64(flagVestingStart) - vestingEnd := viper.GetInt64(flagVestingEnd) - vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt)) - if err != nil { - return err - } - - genFile := config.GenesisFile() - if !common.FileExists(genFile) { - return fmt.Errorf("%s does not exist, run `terrad init` first", genFile) - } - - genDoc, err := LoadGenesisDoc(cdc, genFile) - if err != nil { - return err - } - - var appState app.GenesisState - if err = cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { - return err - } - - appState, err = addGenesisAccount(cdc, appState, addr, coins, vestingAmt, vestingStart, vestingEnd) - if err != nil { - return err - } - - appStateJSON, err := cdc.MarshalJSON(appState) - if err != nil { - return err - } - - return ExportGenesisFile(genFile, genDoc.ChainID, nil, appStateJSON) - }, - } - - cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") - cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") - cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") - cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") - - return cmd -} - -func addGenesisAccount( - cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress, - coins, vestingAmt sdk.Coins, vestingStart, vestingEnd int64, -) (app.GenesisState, error) { - - for _, stateAcc := range appState.Accounts { - if stateAcc.Address.Equals(addr) { - return appState, fmt.Errorf("the application state already contains account %v", addr) - } - } - - acc := auth.NewBaseAccountWithAddress(addr) - acc.Coins = coins - - if !vestingAmt.IsZero() { - var vacc auth.VestingAccount - - bvacc := &auth.BaseVestingAccount{ - BaseAccount: &acc, - OriginalVesting: vestingAmt, - EndTime: vestingEnd, - } - - if bvacc.OriginalVesting.IsAllGT(acc.Coins) { - return appState, fmt.Errorf("vesting amount cannot be greater than total amount") - } - if vestingStart >= vestingEnd { - return appState, fmt.Errorf("vesting start time must before end time") - } - - if vestingStart != 0 { - vacc = &auth.ContinuousVestingAccount{ - BaseVestingAccount: bvacc, - StartTime: vestingStart, - } - } else { - vacc = &auth.DelayedVestingAccount{ - BaseVestingAccount: bvacc, - } - } - - appState.Accounts = append(appState.Accounts, app.NewGenesisAccountI(vacc)) - } else { - appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc)) - } - - return appState, nil -} diff --git a/cmd/init/genesis_accts_test.go b/cmd/init/genesis_accts_test.go deleted file mode 100644 index 5f7fe9f24..000000000 --- a/cmd/init/genesis_accts_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package init - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestAddGenesisAccount(t *testing.T) { - cdc := codec.New() - addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - type args struct { - appState app.GenesisState - addr sdk.AccAddress - coins sdk.Coins - vestingAmt sdk.Coins - vestingStart int64 - vestingEnd int64 - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - "valid account", - args{ - app.GenesisState{}, - addr1, - sdk.NewCoins(), - sdk.NewCoins(), - 0, - 0, - }, - false, - }, - { - "dup account", - args{ - app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}}, - addr1, - sdk.NewCoins(), - sdk.NewCoins(), - 0, - 0, - }, - true, - }, - { - "invalid vesting amount", - args{ - app.GenesisState{}, - addr1, - sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), - sdk.NewCoins(sdk.NewInt64Coin("stake", 100)), - 0, - 0, - }, - true, - }, - { - "invalid vesting times", - args{ - app.GenesisState{}, - addr1, - sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), - sdk.NewCoins(sdk.NewInt64Coin("stake", 50)), - 1654668078, - 1554668078, - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := addGenesisAccount( - cdc, tt.args.appState, tt.args.addr, tt.args.coins, - tt.args.vestingAmt, tt.args.vestingStart, tt.args.vestingEnd, - ) - require.Equal(t, tt.wantErr, (err != nil)) - }) - } -} diff --git a/cmd/init/gentx.go b/cmd/init/gentx.go deleted file mode 100644 index f93dcaafa..000000000 --- a/cmd/init/gentx.go +++ /dev/null @@ -1,301 +0,0 @@ -package init - -// DONTCOVER - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/common" - - "github.com/terra-project/core/app" - "github.com/terra-project/core/types/assets" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/staking/client/cli" -) - -var ( - defaultTokens = sdk.TokensFromTendermintPower(100) - defaultAmount = defaultTokens.String() + assets.MicroLunaDenom - defaultCommissionRate = "0.1" - defaultCommissionMaxRate = "0.2" - defaultCommissionMaxChangeRate = "0.01" - defaultMinSelfDelegation = "1" -) - -// GenTxCmd builds the terrad gentx command. -// nolint: errcheck -func GenTxCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "gentx", - Short: "Generate a genesis tx carrying a self delegation", - Args: cobra.NoArgs, - Long: fmt.Sprintf(`This command is an alias of the 'terrad tx create-validator' command'. - -It creates a genesis piece carrying a self delegation with the -following delegation and commission default parameters: - - delegation amount: %s - commission rate: %s - commission max rate: %s - commission max change rate: %s - minimum self delegation: %s -`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation), - RunE: func(cmd *cobra.Command, args []string) error { - - config := ctx.Config - config.SetRoot(viper.GetString(tmcli.HomeFlag)) - nodeID, valPubKey, err := InitializeNodeValidatorFiles(ctx.Config) - if err != nil { - return err - } - - // Read --nodeID, if empty take it from priv_validator.json - if nodeIDString := viper.GetString(cli.FlagNodeID); nodeIDString != "" { - nodeID = nodeIDString - } - - ip := viper.GetString(cli.FlagIP) - if ip == "" { - fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ - "the tx's memo field will be unset") - } - - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) - if err != nil { - return err - } - - genesisState := app.GenesisState{} - if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { - return err - } - - if err = app.TerraValidateGenesisState(genesisState); err != nil { - return err - } - - kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome)) - if err != nil { - return err - } - - name := viper.GetString(client.FlagName) - key, err := kb.Get(name) - if err != nil { - return err - } - - // Read --pubkey, if empty take it from priv_validator.json - if valPubKeyString := viper.GetString(cli.FlagPubKey); valPubKeyString != "" { - valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString) - if err != nil { - return err - } - } - - website := viper.GetString(cli.FlagWebsite) - details := viper.GetString(cli.FlagDetails) - identity := viper.GetString(cli.FlagIdentity) - - // Set flags for creating gentx - prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey, website, details, identity) - - // Fetch the amount of coins staked - amount := viper.GetString(cli.FlagAmount) - coins, err := sdk.ParseCoins(amount) - if err != nil { - return err - } - - err = accountInGenesis(genesisState, key.GetAddress(), coins) - if err != nil { - return err - } - - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext().WithCodec(cdc) - - // XXX: Set the generate-only flag here after the CLI context has - // been created. This allows the from name/key to be correctly populated. - // - // TODO: Consider removing the manual setting of generate-only in - // favor of a 'gentx' flag in the create-validator command. - viper.Set(client.FlagGenerateOnly, true) - - // create a 'create-validator' message - txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr) - if err != nil { - return err - } - - // write the unsigned transaction to the buffer - w := bytes.NewBuffer([]byte{}) - cliCtx = cliCtx.WithOutput(w) - if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil { - return err - } - - // read the transaction - stdTx, err := readUnsignedGenTxFile(cdc, w) - if err != nil { - return err - } - - // sign the transaction and write it to the output file - signedTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, false, true) - if err != nil { - return err - } - - // Fetch output file name - outputDocument := viper.GetString(client.FlagOutputDocument) - if outputDocument == "" { - outputDocument, err = makeOutputFilepath(config.RootDir, nodeID) - if err != nil { - return err - } - } - - if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument) - return nil - }, - } - - ip, _ := server.ExternalIP() - - cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") - cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") - cmd.Flags().String(client.FlagOutputDocument, "", - "write the genesis transaction JSON document to the given file instead of the default location") - cmd.Flags().String(cli.FlagIP, ip, "The node's public IP") - cmd.Flags().String(cli.FlagNodeID, "", "The node's NodeID") - cmd.Flags().String(cli.FlagWebsite, "", "The validator's (optional) website") - cmd.Flags().String(cli.FlagDetails, "", "The validator's (optional) details") - cmd.Flags().String(cli.FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") - cmd.Flags().AddFlagSet(cli.FsCommissionCreate) - cmd.Flags().AddFlagSet(cli.FsMinSelfDelegation) - cmd.Flags().AddFlagSet(cli.FsAmount) - cmd.Flags().AddFlagSet(cli.FsPk) - cmd.MarkFlagRequired(client.FlagName) - return cmd -} - -func accountInGenesis(genesisState app.GenesisState, key sdk.AccAddress, coins sdk.Coins) error { - accountIsInGenesis := false - bondDenom := genesisState.StakingData.Params.BondDenom - - // Check if the account is in genesis - for _, acc := range genesisState.Accounts { - // Ensure that account is in genesis - if acc.Address.Equals(key) { - - // Ensure account contains enough funds of default bond denom - if coins.AmountOf(bondDenom).GT(acc.Coins.AmountOf(bondDenom)) { - return fmt.Errorf( - "account %v is in genesis, but it only has %v%v available to stake, not %v%v", - key.String(), acc.Coins.AmountOf(bondDenom), bondDenom, coins.AmountOf(bondDenom), bondDenom, - ) - } - accountIsInGenesis = true - break - } - } - - if accountIsInGenesis { - return nil - } - - return fmt.Errorf("account %s in not in the app_state.accounts array of genesis.json", key) -} - -func prepareFlagsForTxCreateValidator( - config *cfg.Config, nodeID, ip, chainID string, valPubKey crypto.PubKey, website, details, identity string, -) { - viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) - viper.Set(client.FlagChainID, chainID) - viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) - viper.Set(cli.FlagNodeID, nodeID) - viper.Set(cli.FlagIP, ip) - viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) - viper.Set(cli.FlagMoniker, config.Moniker) - viper.Set(cli.FlagWebsite, website) - viper.Set(cli.FlagDetails, details) - viper.Set(cli.FlagIdentity, identity) - - if config.Moniker == "" { - viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) - } - if viper.GetString(cli.FlagAmount) == "" { - viper.Set(cli.FlagAmount, defaultAmount) - } - if viper.GetString(cli.FlagCommissionRate) == "" { - viper.Set(cli.FlagCommissionRate, defaultCommissionRate) - } - if viper.GetString(cli.FlagCommissionMaxRate) == "" { - viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) - } - if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" { - viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) - } - if viper.GetString(cli.FlagMinSelfDelegation) == "" { - viper.Set(cli.FlagMinSelfDelegation, defaultMinSelfDelegation) - } -} - -func makeOutputFilepath(rootDir, nodeID string) (string, error) { - writePath := filepath.Join(rootDir, "config", "gentx") - if err := common.EnsureDir(writePath, 0700); err != nil { - return "", err - } - return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil -} - -func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) { - var stdTx auth.StdTx - bytes, err := ioutil.ReadAll(r) - if err != nil { - return stdTx, err - } - err = cdc.UnmarshalJSON(bytes, &stdTx) - return stdTx, err -} - -// nolint: errcheck -func writeSignedGenTx(cdc *codec.Codec, outputDocument string, tx auth.StdTx) error { - outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer outputFile.Close() - json, err := cdc.MarshalJSON(tx) - if err != nil { - return err - } - _, err = fmt.Fprintf(outputFile, "%s\n", json) - return err -} diff --git a/cmd/init/gentx_test.go b/cmd/init/gentx_test.go deleted file mode 100644 index d99a562fc..000000000 --- a/cmd/init/gentx_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package init - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - "github.com/spf13/viper" - "github.com/stretchr/testify/require" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/log" -) - -func Test_prepareFlagsForTxCreateValidator(t *testing.T) { - defer server.SetupViper(t)() - defer setupClientHome(t)() - config, err := tcmd.ParseConfig() - require.Nil(t, err) - logger := log.NewNopLogger() - ctx := server.NewContext(config, logger) - - valPubKey, _ := sdk.GetConsPubKeyBech32("cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a") - - type args struct { - config *cfg.Config - nodeID string - ip string - chainID string - valPubKey crypto.PubKey - website string - details string - identity string - } - - type extraParams struct { - amount string - commissionRate string - commissionMaxRate string - commissionMaxChangeRate string - minSelfDelegation string - } - - type testcase struct { - name string - args args - } - - runTest := func(t *testing.T, tt testcase, params extraParams) { - prepareFlagsForTxCreateValidator(tt.args.config, tt.args.nodeID, tt.args.ip, tt.args.chainID, tt.args.valPubKey, tt.args.website, tt.args.details, tt.args.identity) - require.Equal(t, tt.args.website, viper.GetString(cli.FlagWebsite)) - require.Equal(t, tt.args.details, viper.GetString(cli.FlagDetails)) - require.Equal(t, tt.args.identity, viper.GetString(cli.FlagIdentity)) - require.Equal(t, params.amount, viper.GetString(cli.FlagAmount)) - require.Equal(t, params.commissionRate, viper.GetString(cli.FlagCommissionRate)) - require.Equal(t, params.commissionMaxRate, viper.GetString(cli.FlagCommissionMaxRate)) - require.Equal(t, params.commissionMaxChangeRate, viper.GetString(cli.FlagCommissionMaxChangeRate)) - require.Equal(t, params.minSelfDelegation, viper.GetString(cli.FlagMinSelfDelegation)) - } - - tests := []testcase{ - {"No parameters", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "", "", ""}}, - {"Optional parameters fed", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "cosmos.network", "details", "identity"}}, - } - - defaultParams := extraParams{defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Run(tt.name, func(t *testing.T) { runTest(t, tt, defaultParams) }) - }) - } - - // Override default params - params := extraParams{"5stake", "1.0", "1.0", "1.0", "1.0"} - viper.Set(cli.FlagAmount, params.amount) - viper.Set(cli.FlagCommissionRate, params.commissionRate) - viper.Set(cli.FlagCommissionMaxRate, params.commissionMaxRate) - viper.Set(cli.FlagCommissionMaxChangeRate, params.commissionMaxChangeRate) - viper.Set(cli.FlagMinSelfDelegation, params.minSelfDelegation) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { runTest(t, tt, params) }) - } -} diff --git a/cmd/init/init.go b/cmd/init/init.go deleted file mode 100644 index 4eafa627a..000000000 --- a/cmd/init/init.go +++ /dev/null @@ -1,96 +0,0 @@ -package init - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/common" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" -) - -const ( - flagOverwrite = "overwrite" - flagClientHome = "home-client" - flagVestingStart = "vesting-start-time" - flagVestingEnd = "vesting-end-time" - flagVestingAmt = "vesting-amount" -) - -type printInfo struct { - Moniker string `json:"moniker"` - ChainID string `json:"chain_id"` - NodeID string `json:"node_id"` - GenTxsDir string `json:"gentxs_dir"` - AppMessage json.RawMessage `json:"app_message"` -} - -func displayInfo(cdc *codec.Codec, info printInfo) error { - out, err := codec.MarshalJSONIndent(cdc, info) - if err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "%s\n", string(out)) // nolint: errcheck - return nil -} - -// InitCmd returns a command that initializes all files needed for Tendermint -// and the respective application. -func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { // nolint: golint - cmd := &cobra.Command{ - Use: "init [moniker]", - Short: "Initialize private validator, p2p, genesis, and application configuration files", - Long: `Initialize validators's and node's configuration files.`, - Args: cobra.ExactArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) - - chainID := viper.GetString(client.FlagChainID) - if chainID == "" { - chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) - } - - nodeID, _, err := InitializeNodeValidatorFiles(config) - if err != nil { - return err - } - - config.Moniker = args[0] - - var appState json.RawMessage - genFile := config.GenesisFile() - - if appState, err = initializeEmptyGenesis(cdc, genFile, chainID, - viper.GetBool(flagOverwrite)); err != nil { - return err - } - - if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil { - return err - } - - toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) - - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - return displayInfo(cdc, toPrint) - }, - } - - cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") - cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - - return cmd -} diff --git a/cmd/init/init_test.go b/cmd/init/init_test.go deleted file mode 100644 index b88458ead..000000000 --- a/cmd/init/init_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package init - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "testing" - "time" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/mock" - "github.com/spf13/viper" - "github.com/stretchr/testify/require" - abciServer "github.com/tendermint/tendermint/abci/server" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/log" -) - -func TestInitCmd(t *testing.T) { - defer server.SetupViper(t)() - defer setupClientHome(t)() - - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - - ctx := server.NewContext(cfg, logger) - cdc := app.MakeCodec() - cmd := InitCmd(ctx, cdc) - - require.NoError(t, cmd.RunE(nil, []string{"terranode-test"})) -} - -func setupClientHome(t *testing.T) func() { - clientDir, err := ioutil.TempDir("", "mock-sdk-cmd") - require.Nil(t, err) - viper.Set(flagClientHome, clientDir) - return func() { - if err := os.RemoveAll(clientDir); err != nil { - // TODO: Handle with #870 - panic(err) - } - } -} - -func TestEmptyState(t *testing.T) { - defer server.SetupViper(t)() - defer setupClientHome(t)() - - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - - ctx := server.NewContext(cfg, logger) - cdc := app.MakeCodec() - - cmd := InitCmd(ctx, cdc) - require.NoError(t, cmd.RunE(nil, []string{"terranode-test"})) - - old := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - cmd = server.ExportCmd(ctx, cdc, nil) - - err = cmd.RunE(nil, nil) - require.NoError(t, err) - - outC := make(chan string) - go func() { - var buf bytes.Buffer - _, err := io.Copy(&buf, r) - require.Nil(t, err) - outC <- buf.String() - }() - - w.Close() - os.Stdout = old - out := <-outC - require.Contains(t, out, "genesis_time") - require.Contains(t, out, "chain_id") - require.Contains(t, out, "consensus_params") - require.Contains(t, out, "validators") - require.Contains(t, out, "app_hash") -} - -func TestStartStandAlone(t *testing.T) { - home, err := ioutil.TempDir("", "mock-sdk-cmd") - require.Nil(t, err) - defer func() { - os.RemoveAll(home) - }() - viper.Set(cli.HomeFlag, home) - defer setupClientHome(t)() - - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - ctx := server.NewContext(cfg, logger) - cdc := app.MakeCodec() - initCmd := InitCmd(ctx, cdc) - require.NoError(t, initCmd.RunE(nil, []string{"terranode-test"})) - - app, err := mock.NewApp(home, logger) - require.Nil(t, err) - svrAddr, _, err := server.FreeTCPAddr() - require.Nil(t, err) - svr, err := abciServer.NewServer(svrAddr, "socket", app) - require.Nil(t, err, "error creating listener") - svr.SetLogger(logger.With("module", "abci-server")) - err = svr.Start() - require.Nil(t, err) - - timer := time.NewTimer(time.Duration(2) * time.Second) - - <-timer.C - err = svr.Stop() - require.Nil(t, err) - -} - -func TestInitNodeValidatorFiles(t *testing.T) { - home, err := ioutil.TempDir("", "mock-sdk-cmd") - require.Nil(t, err) - defer func() { - os.RemoveAll(home) - }() - viper.Set(cli.HomeFlag, home) - viper.Set(client.FlagName, "moniker") - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - nodeID, valPubKey, err := InitializeNodeValidatorFiles(cfg) - require.Nil(t, err) - require.NotEqual(t, "", nodeID) - require.NotEqual(t, 0, len(valPubKey.Bytes())) -} diff --git a/cmd/init/utils.go b/cmd/init/utils.go deleted file mode 100644 index 675d63e30..000000000 --- a/cmd/init/utils.go +++ /dev/null @@ -1,115 +0,0 @@ -package init - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "path/filepath" - "time" - - amino "github.com/tendermint/go-amino" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/privval" - "github.com/tendermint/tendermint/types" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" -) - -// ExportGenesisFile creates and writes the genesis configuration to disk. An -// error is returned if building or writing the configuration to file fails. -func ExportGenesisFile( - genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage, -) error { - - genDoc := types.GenesisDoc{ - ChainID: chainID, - Validators: validators, - AppState: appState, - } - - if err := genDoc.ValidateAndComplete(); err != nil { - return err - } - - return genDoc.SaveAs(genFile) -} - -// ExportGenesisFileWithTime creates and writes the genesis configuration to disk. -// An error is returned if building or writing the configuration to file fails. -func ExportGenesisFileWithTime( - genFile, chainID string, validators []types.GenesisValidator, - appState json.RawMessage, genTime time.Time, -) error { - - genDoc := types.GenesisDoc{ - GenesisTime: genTime, - ChainID: chainID, - Validators: validators, - AppState: appState, - } - - if err := genDoc.ValidateAndComplete(); err != nil { - return err - } - - return genDoc.SaveAs(genFile) -} - -// InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles( - config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error, -) { - - nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) - if err != nil { - return nodeID, valPubKey, err - } - - nodeID = string(nodeKey.ID()) - server.UpgradeOldPrivValFile(config) - - pvKeyFile := config.PrivValidatorKeyFile() - if err := common.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { - return nodeID, valPubKey, nil - } - - pvStateFile := config.PrivValidatorStateFile() - if err := common.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { - return nodeID, valPubKey, nil - } - - valPubKey = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey() - - return nodeID, valPubKey, nil -} - -// LoadGenesisDoc reads and unmarshals GenesisDoc from the given file. -func LoadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { - genContents, err := ioutil.ReadFile(genFile) - if err != nil { - return genDoc, err - } - - if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil { - return genDoc, err - } - - return genDoc, err -} - -func initializeEmptyGenesis( - cdc *codec.Codec, genFile, chainID string, overwrite bool, -) (appState json.RawMessage, err error) { - - if !overwrite && common.FileExists(genFile) { - return nil, fmt.Errorf("genesis.json file already exists: %v", genFile) - } - - return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState()) -} diff --git a/cmd/init/utils_test.go b/cmd/init/utils_test.go deleted file mode 100644 index 3a5a13690..000000000 --- a/cmd/init/utils_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package init - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "testing" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/tests" - - "github.com/stretchr/testify/require" -) - -func TestExportGenesisFileWithTime(t *testing.T) { - t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - - fname := filepath.Join(dir, "genesis.json") - require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now())) -} - -func TestLoadGenesisDoc(t *testing.T) { - t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - - fname := filepath.Join(dir, "genesis.json") - require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now())) - - _, err := LoadGenesisDoc(codec.Cdc, fname) - require.NoError(t, err) - - // Non-existing file - _, err = LoadGenesisDoc(codec.Cdc, "non-existing-file") - require.Error(t, err) - - malformedFilename := filepath.Join(dir, "malformed") - malformedFile, err := os.Create(malformedFilename) - require.NoError(t, err) - fmt.Fprint(malformedFile, "invalidjson") - malformedFile.Close() - // Non-existing file - _, err = LoadGenesisDoc(codec.Cdc, malformedFilename) - require.Error(t, err) -} diff --git a/cmd/init/validate_genesis.go b/cmd/init/validate_genesis.go deleted file mode 100644 index c12b987f4..000000000 --- a/cmd/init/validate_genesis.go +++ /dev/null @@ -1,52 +0,0 @@ -package init - -import ( - "fmt" - "os" - - "github.com/terra-project/core/app" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - "github.com/spf13/cobra" - "github.com/tendermint/tendermint/types" -) - -// Validate genesis command takes -func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "validate-genesis [file]", - Args: cobra.RangeArgs(0, 1), - Short: "validates the genesis file at the default location or at the location passed as an arg", - RunE: func(cmd *cobra.Command, args []string) (err error) { - - // Load default if passed no args, otherwise load passed file - var genesis string - if len(args) == 0 { - genesis = ctx.Config.GenesisFile() - } else { - genesis = args[0] - } - - //nolint - fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis) - - var genDoc types.GenesisDoc - if genDoc, err = LoadGenesisDoc(cdc, genesis); err != nil { - return fmt.Errorf("Error loading genesis doc from %s: %s", genesis, err.Error()) - } - - var genstate app.GenesisState - if err = cdc.UnmarshalJSON(genDoc.AppState, &genstate); err != nil { - return fmt.Errorf("Error unmarshaling genesis doc %s: %s", genesis, err.Error()) - } - - if err = app.TerraValidateGenesisState(genstate); err != nil { - return fmt.Errorf("Error validating genesis file %s: %s", genesis, err.Error()) - } - - fmt.Printf("File at %s is a valid genesis file for terrad\n", genesis) - return nil - }, - } -} diff --git a/cmd/terracli/main.go b/cmd/terracli/main.go old mode 100755 new mode 100644 index 4deb12cf0..a32101e6e --- a/cmd/terracli/main.go +++ b/cmd/terracli/main.go @@ -2,67 +2,33 @@ package main import ( "fmt" - "net/http" "os" "path" - - "github.com/rakyll/statik/fs" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/libs/cli" - - "github.com/terra-project/core/app" - "github.com/terra-project/core/client/keys" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/version" + "strings" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/lcd" + "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" - at "github.com/cosmos/cosmos-sdk/x/auth" - - txcustom "github.com/terra-project/core/client/tx" - - authcustom "github.com/terra-project/core/x/auth/client/rest" - dist "github.com/terra-project/core/x/distribution/client/rest" - slashing "github.com/terra-project/core/x/slashing/client/rest" - staking "github.com/terra-project/core/x/staking/client/rest" - - budget "github.com/terra-project/core/x/budget/client/rest" - market "github.com/terra-project/core/x/market/client/rest" - oracle "github.com/terra-project/core/x/oracle/client/rest" - pay "github.com/terra-project/core/x/pay/client/rest" - treasury "github.com/terra-project/core/x/treasury/client/rest" - - bud "github.com/terra-project/core/x/budget" - mkt "github.com/terra-project/core/x/market" - ora "github.com/terra-project/core/x/oracle" - tre "github.com/terra-project/core/x/treasury" - - dt "github.com/cosmos/cosmos-sdk/x/distribution" - sl "github.com/cosmos/cosmos-sdk/x/slashing" - st "github.com/cosmos/cosmos-sdk/x/staking" - - auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - + "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - paycmd "github.com/terra-project/core/x/pay/client/cli" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - budgetClient "github.com/terra-project/core/x/budget/client" - distClient "github.com/terra-project/core/x/distribution/client" - marketClient "github.com/terra-project/core/x/market/client" - oracleClient "github.com/terra-project/core/x/oracle/client" - slashingClient "github.com/terra-project/core/x/slashing/client" - stakingClient "github.com/terra-project/core/x/staking/client" - treasuryClient "github.com/terra-project/core/x/treasury/client" + "github.com/spf13/cobra" + "github.com/spf13/viper" - crisisClient "github.com/cosmos/cosmos-sdk/x/crisis/client" + "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/cli" - _ "github.com/terra-project/core/client/lcd/statik" + "github.com/terra-project/core/app" + "github.com/terra-project/core/client/lcd" + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/auth" + + tauthcmd "github.com/terra-project/core/x/auth/client/cli" + tauthrest "github.com/terra-project/core/x/auth/client/rest" + "github.com/terra-project/core/x/bank" + tbankcmd "github.com/terra-project/core/x/bank/client/cli" ) func main() { @@ -74,30 +40,17 @@ func main() { // Read in the configuration file for the sdk config := sdk.GetConfig() - config.SetCoinType(util.CoinType) - config.SetFullFundraiserPath(util.FullFundraiserPath) - config.SetBech32PrefixForAccount(util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(util.Bech32PrefixValAddr, util.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub) + config.SetCoinType(core.CoinType) + config.SetFullFundraiserPath(core.FullFundraiserPath) + config.SetBech32PrefixForAccount(core.Bech32PrefixAccAddr, core.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(core.Bech32PrefixValAddr, core.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(core.Bech32PrefixConsAddr, core.Bech32PrefixConsPub) config.Seal() // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc - // Module clients hold cli commnads (tx,query) and lcd routes - // TODO: Make the lcd command take a list of ModuleClient - mc := []sdk.ModuleClients{ - distClient.NewModuleClient(dt.StoreKey, cdc), - stakingClient.NewModuleClient(st.StoreKey, cdc), - slashingClient.NewModuleClient(sl.StoreKey, cdc), - oracleClient.NewModuleClient(ora.StoreKey, cdc), - treasuryClient.NewModuleClient(tre.StoreKey, cdc), - budgetClient.NewModuleClient(bud.StoreKey, cdc), - marketClient.NewModuleClient(mkt.StoreKey, cdc), - crisisClient.NewModuleClient(sl.StoreKey, cdc), - } - rootCmd := &cobra.Command{ Use: "terracli", Short: "Command line interface for interacting with terrad", @@ -113,18 +66,20 @@ func main() { rootCmd.AddCommand( rpc.StatusCommand(), client.ConfigCmd(app.DefaultCLIHome), - queryCmd(cdc, mc), - txCmd(cdc, mc), + queryCmd(cdc), + txCmd(cdc), client.LineBreak, lcd.ServeCommand(cdc, registerRoutes), client.LineBreak, keys.Commands(), client.LineBreak, - version.VersionCmd, + version.Cmd, client.NewCompletionCmd(rootCmd, true), ) - // Add flags and prefix all env exposed with GA + changeDescription(rootCmd) + + // Add flags and prefix all env exposed with TE executor := cli.PrepareMainCmd(rootCmd, "TE", app.DefaultCLIHome) err := executor.Execute() @@ -134,7 +89,23 @@ func main() { } } -func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { +// change cosmos prefix to terra +func changeDescription(command *cobra.Command) { + childCommands := command.Commands() + if len(childCommands) == 0 { + return + } + + for _, childCommand := range childCommands { + childCommand.Long = strings.ReplaceAll(childCommand.Long, "cosmos", "terra") + childCommand.Long = strings.ReplaceAll(childCommand.Long, "", "terracli") + childCommand.Long = strings.ReplaceAll(childCommand.Long, "Atoms", "Lunas") + + changeDescription(childCommand) + } +} + +func queryCmd(cdc *amino.Codec) *cobra.Command { queryCmd := &cobra.Command{ Use: "query", Aliases: []string{"q"}, @@ -142,83 +113,65 @@ func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { } queryCmd.AddCommand( + authcmd.GetAccountCmd(cdc), + client.LineBreak, rpc.ValidatorCommand(cdc), rpc.BlockCommand(), - tx.SearchTxCmd(cdc), - tx.QueryTxCmd(cdc), + authcmd.QueryTxsByEventsCmd(cdc), + authcmd.QueryTxCmd(cdc), client.LineBreak, - authcmd.GetAccountCmd(at.StoreKey, cdc), ) - for _, m := range mc { - mQueryCmd := m.GetQueryCmd() - if mQueryCmd != nil { - queryCmd.AddCommand(mQueryCmd) - } - } + // add modules' query commands + app.ModuleBasics.AddQueryCommands(queryCmd, cdc) return queryCmd } -func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { +func txCmd(cdc *amino.Codec) *cobra.Command { txCmd := &cobra.Command{ Use: "tx", Short: "Transactions subcommands", } txCmd.AddCommand( - paycmd.SendTxCmd(cdc), + tbankcmd.SendTxCmd(cdc), client.LineBreak, authcmd.GetSignCommand(cdc), authcmd.GetMultiSignCommand(cdc), - tx.GetBroadcastCommand(cdc), - tx.GetEncodeCommand(cdc), client.LineBreak, + authcmd.GetBroadcastCommand(cdc), + authcmd.GetEncodeCommand(cdc), + client.LineBreak, + tauthcmd.GetTxFeesEstimateCommand(cdc), ) - for _, m := range mc { - txCmd.AddCommand(m.GetTxCmd()) + // add modules' tx commands + app.ModuleBasics.AddTxCommands(txCmd, cdc) + + // remove auth and bank commands as they're mounted under the root tx command + var cmdsToRemove []*cobra.Command + + for _, cmd := range txCmd.Commands() { + if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName { + cmdsToRemove = append(cmdsToRemove, cmd) + } } - return txCmd -} + txCmd.RemoveCommand(cmdsToRemove...) -// CLIVersionRequestHandler cli version REST handler endpoint -func CLIVersionRequestHandler(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))) + return txCmd } // registerRoutes registers the routes from the different modules for the LCD. // NOTE: details on the routes added for each module are in the module documentation // NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go func registerRoutes(rs *lcd.RestServer) { - - rs.Mux.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") - - registerSwaggerUI(rs) - rpc.RegisterRoutes(rs.CliCtx, rs.Mux) - txcustom.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey) - dist.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, dt.StoreKey) - staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - - authcustom.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - pay.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - oracle.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - treasury.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - market.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - budget.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) -} - -func registerSwaggerUI(rs *lcd.RestServer) { - statikFS, err := fs.New() - if err != nil { - panic(err) - } - staticServer := http.FileServer(statikFS) - rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) + client.RegisterRoutes(rs.CliCtx, rs.Mux) + authrest.RegisterTxRoutes(rs.CliCtx, rs.Mux) + tauthrest.RegisterTxRoutes(rs.CliCtx, rs.Mux) + tauthrest.RegisterRoutes(rs.CliCtx, rs.Mux) + app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) } func initConfig(cmd *cobra.Command) error { diff --git a/cmd/terrad/main.go b/cmd/terrad/main.go old mode 100755 new mode 100644 index 9c581a722..300be5ec9 --- a/cmd/terrad/main.go +++ b/cmd/terrad/main.go @@ -4,43 +4,44 @@ import ( "encoding/json" "io" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/version" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/store" - "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/cli" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" "github.com/terra-project/core/app" - terraInit "github.com/terra-project/core/cmd/init" + core "github.com/terra-project/core/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - terraserver "github.com/terra-project/core/server" + "github.com/cosmos/cosmos-sdk/x/genaccounts" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + genaccscli "github.com/terra-project/core/x/genaccounts/client/cli" + "github.com/terra-project/core/x/staking" ) -const flagAssertInvariantsBlockly = "assert-invariants-blockly" +// terrad custom flags +const flagInvCheckPeriod = "inv-check-period" -var assertInvariantsBlockly bool +var invCheckPeriod uint func main() { cdc := app.MakeCodec() config := sdk.GetConfig() - config.SetCoinType(util.CoinType) - config.SetFullFundraiserPath(util.FullFundraiserPath) - config.SetBech32PrefixForAccount(util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(util.Bech32PrefixValAddr, util.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub) + config.SetCoinType(core.CoinType) + config.SetFullFundraiserPath(core.FullFundraiserPath) + config.SetBech32PrefixForAccount(core.Bech32PrefixAccAddr, core.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(core.Bech32PrefixValAddr, core.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(core.Bech32PrefixConsAddr, core.Bech32PrefixConsPub) config.Seal() ctx := server.NewDefaultContext() @@ -48,51 +49,52 @@ func main() { rootCmd := &cobra.Command{ Use: "terrad", Short: "Terra Daemon (server)", - PersistentPreRunE: terraserver.PersistentPreRunEFn(ctx), + PersistentPreRunE: server.PersistentPreRunEFn(ctx), } - rootCmd.AddCommand(terraInit.InitCmd(ctx, cdc)) - rootCmd.AddCommand(terraInit.CollectGenTxsCmd(ctx, cdc)) - rootCmd.AddCommand(terraInit.TestnetFilesCmd(ctx, cdc)) - rootCmd.AddCommand(terraInit.GenTxCmd(ctx, cdc)) - rootCmd.AddCommand(terraInit.AddGenesisAccountCmd(ctx, cdc)) - rootCmd.AddCommand(terraInit.ValidateGenesisCmd(ctx, cdc)) - rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true)) - // preempting version command - rootCmd.AddCommand(version.VersionCmd) + rootCmd.AddCommand(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)) + rootCmd.AddCommand(genutilcli.CollectGenTxsCmd(ctx, cdc, genaccounts.AppModuleBasic{}, app.DefaultNodeHome)) + rootCmd.AddCommand(genutilcli.GenTxCmd(ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, + genaccounts.AppModuleBasic{}, app.DefaultNodeHome, app.DefaultCLIHome)) + rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics)) + rootCmd.AddCommand(genaccscli.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome)) + rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true)) + rootCmd.AddCommand(testnetCmd(ctx, cdc, app.ModuleBasics, genaccounts.AppModuleBasic{})) + rootCmd.AddCommand(replayCmd()) server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) // prepare and add flags executor := cli.PrepareBaseCmd(rootCmd, "TE", app.DefaultNodeHome) - rootCmd.PersistentFlags().BoolVar(&assertInvariantsBlockly, flagAssertInvariantsBlockly, - false, "Assert registered invariants on a blockly basis") + rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod, + 0, "Assert registered invariants every N blocks") err := executor.Execute() if err != nil { - // handle with #870 panic(err) } } func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { return app.NewTerraApp( - logger, db, traceStore, true, assertInvariantsBlockly, + logger, db, traceStore, true, invCheckPeriod, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), + baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))), ) } func exportAppStateAndTMValidators( logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, ) (json.RawMessage, []tmtypes.GenesisValidator, error) { + if height != -1 { - tApp := app.NewTerraApp(logger, db, traceStore, false, false) + tApp := app.NewTerraApp(logger, db, traceStore, false, uint(1)) err := tApp.LoadHeight(height) if err != nil { return nil, nil, err } return tApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } - tApp := app.NewTerraApp(logger, db, traceStore, true, false) + tApp := app.NewTerraApp(logger, db, traceStore, true, uint(1)) return tApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } diff --git a/cmd/terrad/replay.go b/cmd/terrad/replay.go new file mode 100644 index 000000000..72900a087 --- /dev/null +++ b/cmd/terrad/replay.go @@ -0,0 +1,187 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "time" + + cpm "github.com/otiai10/copy" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/proxy" + tmsm "github.com/tendermint/tendermint/state" + tmstore "github.com/tendermint/tendermint/store" + tm "github.com/tendermint/tendermint/types" + + "github.com/terra-project/core/app" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func replayCmd() *cobra.Command { + return &cobra.Command{ + Use: "replay ", + Short: "Replay terra transactions", + RunE: func(_ *cobra.Command, args []string) error { + return replayTxs(args[0]) + }, + Args: cobra.ExactArgs(1), + } +} + +func replayTxs(rootDir string) error { + + if false { + // Copy the rootDir to a new directory, to preserve the old one. + fmt.Fprintln(os.Stderr, "Copying rootdir over") + oldRootDir := rootDir + rootDir = oldRootDir + "_replay" + if cmn.FileExists(rootDir) { + cmn.Exit(fmt.Sprintf("temporary copy dir %v already exists", rootDir)) + } + if err := cpm.Copy(oldRootDir, rootDir); err != nil { + return err + } + } + + configDir := filepath.Join(rootDir, "config") + dataDir := filepath.Join(rootDir, "data") + ctx := server.NewDefaultContext() + + // App DB + fmt.Fprintln(os.Stderr, "Opening app database") + appDB, err := sdk.NewLevelDB("application", dataDir) + if err != nil { + return err + } + + // TM DB + fmt.Fprintln(os.Stderr, "Opening tendermint state database") + tmDB, err := sdk.NewLevelDB("state", dataDir) + if err != nil { + return err + } + + // Blockchain DB + fmt.Fprintln(os.Stderr, "Opening blockstore database") + bcDB, err := sdk.NewLevelDB("blockstore", dataDir) + if err != nil { + return err + } + + // TraceStore + var traceStoreWriter io.Writer + var traceStoreDir = filepath.Join(dataDir, "trace.log") + traceStoreWriter, err = os.OpenFile( + traceStoreDir, + os.O_WRONLY|os.O_APPEND|os.O_CREATE, + 0666, + ) + if err != nil { + return err + } + + // Application + fmt.Fprintln(os.Stderr, "Creating application") + myapp := app.NewTerraApp( + ctx.Logger, appDB, traceStoreWriter, true, uint(1), + baseapp.SetPruning(store.PruneEverything), // nothing + ) + + // Genesis + var genDocPath = filepath.Join(configDir, "genesis.json") + genDoc, err := tm.GenesisDocFromFile(genDocPath) + if err != nil { + return err + } + genState, err := tmsm.MakeGenesisState(genDoc) + if err != nil { + return err + } + + cc := proxy.NewLocalClientCreator(myapp) + proxyApp := proxy.NewAppConns(cc) + err = proxyApp.Start() + if err != nil { + return err + } + defer func() { + _ = proxyApp.Stop() + }() + + state := tmsm.LoadState(tmDB) + if state.LastBlockHeight == 0 { + // Send InitChain msg + fmt.Fprintln(os.Stderr, "Sending InitChain msg") + validators := tm.TM2PB.ValidatorUpdates(genState.Validators) + csParams := tm.TM2PB.ConsensusParams(genDoc.ConsensusParams) + req := abci.RequestInitChain{ + Time: genDoc.GenesisTime, + ChainId: genDoc.ChainID, + ConsensusParams: csParams, + Validators: validators, + AppStateBytes: genDoc.AppState, + } + res, err := proxyApp.Consensus().InitChainSync(req) + if err != nil { + return err + } + newValidatorz, err := tm.PB2TM.ValidatorUpdates(res.Validators) + if err != nil { + return err + } + newValidators := tm.NewValidatorSet(newValidatorz) + + // Take the genesis state. + state = genState + state.Validators = newValidators + state.NextValidators = newValidators + } + + // Create executor + fmt.Fprintln(os.Stderr, "Creating block executor") + blockExec := tmsm.NewBlockExecutor(tmDB, ctx.Logger, proxyApp.Consensus(), nil, tmsm.MockEvidencePool{}) + + // Create block store + fmt.Fprintln(os.Stderr, "Creating block store") + blockStore := tmstore.NewBlockStore(bcDB) + + tz := []time.Duration{0, 0, 0} + for i := int(state.LastBlockHeight) + 1; ; i++ { + fmt.Fprintln(os.Stderr, "Running block ", i) + t1 := time.Now() + + // Apply block + fmt.Printf("loading and applying block %d\n", i) + blockmeta := blockStore.LoadBlockMeta(int64(i)) + if blockmeta == nil { + fmt.Printf("Couldn't find block meta %d... done?\n", i) + return nil + } + block := blockStore.LoadBlock(int64(i)) + if block == nil { + return fmt.Errorf("couldn't find block %d", i) + } + + t2 := time.Now() + + state, err = blockExec.ApplyBlock(state, blockmeta.BlockID, block) + if err != nil { + return err + } + + t3 := time.Now() + tz[0] += t2.Sub(t1) + tz[1] += t3.Sub(t2) + + fmt.Fprintf(os.Stderr, "new app hash: %X\n", state.AppHash) + fmt.Fprintln(os.Stderr, tz) + } +} diff --git a/cmd/init/testnet.go b/cmd/terrad/testnet.go similarity index 53% rename from cmd/init/testnet.go rename to cmd/terrad/testnet.go index c821a2f47..4331f771a 100644 --- a/cmd/init/testnet.go +++ b/cmd/terrad/testnet.go @@ -1,27 +1,15 @@ -package init +package main // DONTCOVER import ( + "bufio" "encoding/json" "fmt" "net" "os" "path/filepath" - "github.com/cosmos/cosmos-sdk/client/keys" - - "github.com/terra-project/core/app" - "github.com/terra-project/core/types/assets" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - srvconfig "github.com/cosmos/cosmos-sdk/server/config" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/spf13/cobra" "github.com/spf13/viper" tmconfig "github.com/tendermint/tendermint/config" @@ -30,7 +18,21 @@ import ( "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/staking" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/genaccounts" + tgenutil "github.com/terra-project/core/x/genutil" ) var ( @@ -38,14 +40,13 @@ var ( flagNumValidators = "v" flagOutputDir = "output-dir" flagNodeDaemonHome = "node-daemon-home" - flagNodeCliHome = "node-cli-home" + flagNodeCLIHome = "node-cli-home" flagStartingIPAddress = "starting-ip-address" ) -const nodeDirPerm = 0755 - // get cmd to initialize all files for tendermint testnet and application -func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { +func testnetCmd(ctx *server.Context, cdc *codec.Codec, + mbm module.BasicManager, genAccIterator genutiltypes.GenesisAccountsIterator) *cobra.Command { cmd := &cobra.Command{ Use: "testnet", @@ -58,48 +59,54 @@ Note, strict routability for addresses is turned off in the config file. Example: terrad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 `, - RunE: func(_ *cobra.Command, _ []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { config := ctx.Config - return initTestnet(config, cdc) + + outputDir := viper.GetString(flagOutputDir) + chainID := viper.GetString(client.FlagChainID) + minGasPrices := viper.GetString(server.FlagMinGasPrices) + nodeDirPrefix := viper.GetString(flagNodeDirPrefix) + nodeDaemonHome := viper.GetString(flagNodeDaemonHome) + nodeCLIHome := viper.GetString(flagNodeCLIHome) + startingIPAddress := viper.GetString(flagStartingIPAddress) + numValidators := viper.GetInt(flagNumValidators) + + // override module codec + *(genutil.ModuleCdc) = *(tgenutil.ModuleCdc) // nolint + + return InitTestnet(cmd, config, cdc, mbm, genAccIterator, outputDir, chainID, + minGasPrices, nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, numValidators) }, } cmd.Flags().Int(flagNumValidators, 4, - "Number of validators to initialize the testnet with", - ) + "Number of validators to initialize the testnet with") cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", - "Directory to store initialization data for the testnet", - ) + "Directory to store initialization data for the testnet") cmd.Flags().String(flagNodeDirPrefix, "node", - "Prefix the directory name for each node with (node results in node0, node1, ...)", - ) + "Prefix the directory name for each node with (node results in node0, node1, ...)") cmd.Flags().String(flagNodeDaemonHome, "terrad", - "Home directory of the node's daemon configuration", - ) - cmd.Flags().String(flagNodeCliHome, "terracli", - "Home directory of the node's cli configuration", - ) + "Home directory of the node's daemon configuration") + cmd.Flags().String(flagNodeCLIHome, "terracli", + "Home directory of the node's cli configuration") cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") - cmd.Flags().String( - client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created", - ) + client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String( - server.FlagMinGasPrices, fmt.Sprintf("0.015%s,0.015%s,0.015%s,0.015%s,0.015%s,0.015%s,0.015%s,0.015%s", assets.MicroLunaDenom, assets.MicroSDRDenom, assets.MicroUSDDenom, assets.MicroKRWDenom, assets.MicroCNYDenom, assets.MicroJPYDenom, assets.MicroEURDenom, assets.MicroGBPDenom), - "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01uluna,0.01usdr)", - ) - + server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", core.MicroLunaDenom), + "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001uluna)") return cmd } -func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { - var chainID string +const nodeDirPerm = 0755 - outDir := viper.GetString(flagOutputDir) - numValidators := viper.GetInt(flagNumValidators) +// Initialize the testnet +func InitTestnet(cmd *cobra.Command, config *tmconfig.Config, cdc *codec.Codec, + mbm module.BasicManager, genAccIterator genutiltypes.GenesisAccountsIterator, + outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome, + nodeCLIHome, startingIPAddress string, numValidators int) error { - chainID = viper.GetString(client.FlagChainID) if chainID == "" { chainID = "chain-" + cmn.RandStr(6) } @@ -109,57 +116,54 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { valPubKeys := make([]crypto.PubKey, numValidators) terraConfig := srvconfig.DefaultConfig() - terraConfig.MinGasPrices = viper.GetString(server.FlagMinGasPrices) + terraConfig.MinGasPrices = minGasPrices var ( - accs []app.GenesisAccount + accs []genaccounts.GenesisAccount genFiles []string ) // generate private keys, node IDs, and initial transactions for i := 0; i < numValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i) - nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome) - nodeCliHomeName := viper.GetString(flagNodeCliHome) - nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) - clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName) - gentxsDir := filepath.Join(outDir, "gentxs") + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome) + gentxsDir := filepath.Join(outputDir, "gentxs") config.SetRoot(nodeDir) + config.RPC.ListenAddress = "tcp://0.0.0.0:26657" - err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outDir) + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) return err } - err = os.MkdirAll(clientDir, nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outDir) + if err := os.MkdirAll(clientDir, nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) return err } monikers = append(monikers, nodeDirName) config.Moniker = nodeDirName - ip, err := getIP(i, viper.GetString(flagStartingIPAddress)) + ip, err := getIP(i, startingIPAddress) if err != nil { - _ = os.RemoveAll(outDir) + _ = os.RemoveAll(outputDir) return err } - nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config) + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config) if err != nil { - _ = os.RemoveAll(outDir) + _ = os.RemoveAll(outputDir) return err } memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) genFiles = append(genFiles, config.GenesisFile()) - buf := client.BufferStdin() + buf := bufio.NewReader(cmd.InOrStdin()) prompt := fmt.Sprintf( - "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass, + "Password for account '%s' (default %s):", nodeDirName, client.DefaultKeyPass, ) keyPass, err := client.GetPassword(prompt, buf) @@ -171,12 +175,12 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { } if keyPass == "" { - keyPass = app.DefaultKeyPass + keyPass = client.DefaultKeyPass } addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true) if err != nil { - _ = os.RemoveAll(outDir) + _ = os.RemoveAll(outputDir) return err } @@ -188,53 +192,51 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { } // save private key seed words - err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint) - if err != nil { + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint); err != nil { return err } - accTokens := sdk.TokensFromTendermintPower(sdk.NewInt(1000).MulRaw(assets.MicroUnit).Int64()) - accStakingTokens := sdk.TokensFromTendermintPower(sdk.NewInt(500).MulRaw(assets.MicroUnit).Int64()) - accs = append(accs, app.GenesisAccount{ + accTokens := sdk.TokensFromConsensusPower(1000) + accStakingTokens := sdk.TokensFromConsensusPower(500) + accs = append(accs, genaccounts.GenesisAccount{ Address: addr, Coins: sdk.Coins{ sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), - sdk.NewCoin(assets.MicroLunaDenom, accStakingTokens), + sdk.NewCoin(core.MicroLunaDenom, accStakingTokens), }, }) - valTokens := sdk.TokensFromTendermintPower(sdk.NewInt(100).MulRaw(assets.MicroUnit).Int64()) + valTokens := sdk.TokensFromConsensusPower(100) msg := staking.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], - sdk.NewCoin(assets.MicroLunaDenom, valTokens), + sdk.NewCoin(core.MicroLunaDenom, valTokens), staking.NewDescription(nodeDirName, "", "", ""), - staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), sdk.OneInt(), ) kb, err := keys.NewKeyBaseFromDir(clientDir) if err != nil { return err } - tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo) - txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb) + tx := authtypes.NewStdTx([]sdk.Msg{msg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) + txBldr := authtypes.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb) - signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false) + signedTx, err := txBldr.SignStdTx(nodeDirName, client.DefaultKeyPass, tx, false) if err != nil { - _ = os.RemoveAll(outDir) + _ = os.RemoveAll(outputDir) return err } txBytes, err := cdc.MarshalJSON(signedTx) if err != nil { - _ = os.RemoveAll(outDir) + _ = os.RemoveAll(outputDir) return err } // gather gentxs folder - err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes) - if err != nil { - _ = os.RemoveAll(outDir) + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes); err != nil { + _ = os.RemoveAll(outputDir) return err } @@ -242,29 +244,29 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { srvconfig.WriteConfigFile(terraConfigFilePath, terraConfig) } - if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil { + if err := initGenFiles(cdc, mbm, chainID, accs, genFiles, numValidators); err != nil { return err } err := collectGenFiles( cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators, - outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome), + outputDir, nodeDirPrefix, nodeDaemonHome, genAccIterator, ) if err != nil { return err } - fmt.Printf("Successfully initialized %d node directories\n", numValidators) + cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators) return nil } -func initGenFiles( - cdc *codec.Codec, chainID string, accs []app.GenesisAccount, - genFiles []string, numValidators int, -) error { +func initGenFiles(cdc *codec.Codec, mbm module.BasicManager, chainID string, + accs []genaccounts.GenesisAccount, genFiles []string, numValidators int) error { + + appGenState := mbm.DefaultGenesis() - appGenState := app.NewDefaultGenesisState() - appGenState.Accounts = accs + // set the accounts in the genesis state + appGenState = genaccounts.SetGenesisStateInAppState(cdc, appGenState, accs) appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) if err != nil { @@ -283,37 +285,36 @@ func initGenFiles( return err } } - return nil } func collectGenFiles( cdc *codec.Codec, config *tmconfig.Config, chainID string, monikers, nodeIDs []string, valPubKeys []crypto.PubKey, - numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string, -) error { + numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, + genAccIterator genutiltypes.GenesisAccountsIterator) error { var appState json.RawMessage genTime := tmtime.Now() for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) - nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) - gentxsDir := filepath.Join(outDir, "gentxs") + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") moniker := monikers[i] config.Moniker = nodeDirName config.SetRoot(nodeDir) nodeID, valPubKey := nodeIDs[i], valPubKeys[i] - initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey) + initCfg := genutil.NewInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey) - genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) if err != nil { return err } - nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator) if err != nil { return err } @@ -326,8 +327,7 @@ func collectGenFiles( genFile := config.GenesisFile() // overwrite each validator's genesis file to have a canonical genesis time - err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime) - if err != nil { + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { return err } } @@ -335,25 +335,28 @@ func collectGenFiles( return nil } -func getIP(i int, startingIPAddr string) (string, error) { - var ( - ip string - err error - ) - +func getIP(i int, startingIPAddr string) (ip string, err error) { if len(startingIPAddr) == 0 { ip, err = server.ExternalIP() if err != nil { return "", err } - } else { - ip, err = calculateIP(startingIPAddr, i) - if err != nil { - return "", err - } + return ip, nil } + return calculateIP(startingIPAddr, i) +} - return ip, nil +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil } func writeFile(name string, dir string, contents []byte) error { @@ -372,16 +375,3 @@ func writeFile(name string, dir string, contents []byte) error { return nil } - -func calculateIP(ip string, i int) (string, error) { - ipv4 := net.ParseIP(ip).To4() - if ipv4 == nil { - return "", fmt.Errorf("%v: non ipv4 address", ip) - } - - for j := 0; j < i; j++ { - ipv4[3]++ - } - - return ipv4.String(), nil -} diff --git a/cmd/terradebug/README.md b/cmd/terradebug/README.md new file mode 100644 index 000000000..a35799ffa --- /dev/null +++ b/cmd/terradebug/README.md @@ -0,0 +1,24 @@ +# Terradebug + +Simple tool for simple debugging. + +We try to accept both hex and base64 formats and provide a useful response. + +Note we often encode bytes as hex in the logs, but as base64 in the JSON. + +## Pubkeys + +The following give the same result: + +``` +terradebug pubkey TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4= +terradebug pubkey 4D94D09DFA8EB22F3D49EA17567230FAD9C5267AF85FCA950B453C02C126164E +``` + +## Txs + +Pass in a hex/base64 tx and get back the full JSON + +``` +terradebug tx +``` diff --git a/cmd/terradebug/hack.go b/cmd/terradebug/hack.go new file mode 100644 index 000000000..a03f6ea70 --- /dev/null +++ b/cmd/terradebug/hack.go @@ -0,0 +1,101 @@ +package main + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + "os" + "path" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/tendermint/tendermint/libs/log" + + terra "github.com/terra-project/core/app" +) + +func runHackCmd(cmd *cobra.Command, args []string) error { + + if len(args) != 1 { + return fmt.Errorf("Expected 1 arg") + } + + // ".terrad" + dataDir := args[0] + dataDir = path.Join(dataDir, "data") + + // load the app + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + db, err := sdk.NewLevelDB("terra", dataDir) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + app, keyMain, keyStaking, stakingKeeper := terra.NewTerraAppUNSAFE( + logger, db, nil, false, 0, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning")))) + + // print some info + id := app.LastCommitID() + lastBlockHeight := app.LastBlockHeight() + fmt.Println("ID", id) + fmt.Println("LastBlockHeight", lastBlockHeight) + + //---------------------------------------------------- + // XXX: start hacking! + //---------------------------------------------------- + // eg. gaia-6001 testnet bug + // We paniced when iterating through the "bypower" keys. + // The following powerKey was there, but the corresponding "trouble" validator did not exist. + // So here we do a binary search on the past states to find when the powerKey first showed up ... + + // operator of the validator the bonds, gets jailed, later unbonds, and then later is still found in the bypower store + trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") + // this is his "bypower" key + powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") + + topHeight := lastBlockHeight + bottomHeight := int64(0) + checkHeight := topHeight + for { + // load the given version of the state + err = app.LoadVersion(checkHeight, keyMain) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + ctx := app.NewContext(true, abci.Header{}) + + // check for the powerkey and the validator from the store + store := ctx.KVStore(keyStaking) + res := store.Get(powerKey) + val, _ := stakingKeeper.GetValidator(ctx, trouble) + fmt.Println("checking height", checkHeight, res, val) + if res == nil { + bottomHeight = checkHeight + } else { + topHeight = checkHeight + } + checkHeight = (topHeight + bottomHeight) / 2 + } +} + +func base64ToPub(b64 string) ed25519.PubKeyEd25519 { + data, _ := base64.StdEncoding.DecodeString(b64) + var pubKey ed25519.PubKeyEd25519 + copy(pubKey[:], data) + return pubKey + +} + +func hexToBytes(h string) []byte { + trouble, _ := hex.DecodeString(h) + return trouble + +} diff --git a/cmd/terradebug/main.go b/cmd/terradebug/main.go new file mode 100644 index 000000000..c115329cf --- /dev/null +++ b/cmd/terradebug/main.go @@ -0,0 +1,260 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + + terra "github.com/terra-project/core/app" + core "github.com/terra-project/core/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func init() { + + config := sdk.GetConfig() + config.SetCoinType(core.CoinType) + config.SetFullFundraiserPath(core.FullFundraiserPath) + config.SetBech32PrefixForAccount(core.Bech32PrefixAccAddr, core.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(core.Bech32PrefixValAddr, core.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(core.Bech32PrefixConsAddr, core.Bech32PrefixConsPub) + config.Seal() + + rootCmd.AddCommand(txCmd) + rootCmd.AddCommand(pubkeyCmd) + rootCmd.AddCommand(addrCmd) + rootCmd.AddCommand(hackCmd) + rootCmd.AddCommand(rawBytesCmd) +} + +var rootCmd = &cobra.Command{ + Use: "terradebug", + Short: "Terra debug tool", + SilenceUsage: true, +} + +var txCmd = &cobra.Command{ + Use: "tx", + Short: "Decode a terra tx from hex or base64", + RunE: runTxCmd, +} + +var pubkeyCmd = &cobra.Command{ + Use: "pubkey", + Short: "Decode a pubkey from hex, base64, or bech32", + RunE: runPubKeyCmd, +} + +var addrCmd = &cobra.Command{ + Use: "addr", + Short: "Convert an address between hex and bech32", + RunE: runAddrCmd, +} + +var hackCmd = &cobra.Command{ + Use: "hack", + Short: "Boilerplate to Hack on an existing state by scripting some Go...", + RunE: runHackCmd, +} + +var rawBytesCmd = &cobra.Command{ + Use: "raw-bytes", + Short: "Convert raw bytes output (eg. [10 21 13 255]) to hex", + RunE: runRawBytesCmd, +} + +func runRawBytesCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("Expected single arg") + } + stringBytes := args[0] + stringBytes = strings.Trim(stringBytes, "[") + stringBytes = strings.Trim(stringBytes, "]") + spl := strings.Split(stringBytes, " ") + + byteArray := []byte{} + for _, s := range spl { + b, err := strconv.Atoi(s) + if err != nil { + return err + } + byteArray = append(byteArray, byte(b)) + } + fmt.Printf("%X\n", byteArray) + return nil +} + +func runPubKeyCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("Expected single arg") + } + + pubkeyString := args[0] + var pubKeyI crypto.PubKey + + // try hex, then base64, then bech32 + pubkeyBytes, err := hex.DecodeString(pubkeyString) + if err != nil { + var err2 error + pubkeyBytes, err2 = base64.StdEncoding.DecodeString(pubkeyString) + if err2 != nil { + var err3 error + pubKeyI, err3 = sdk.GetAccPubKeyBech32(pubkeyString) + if err3 != nil { + var err4 error + pubKeyI, err4 = sdk.GetValPubKeyBech32(pubkeyString) + + if err4 != nil { + var err5 error + pubKeyI, err5 = sdk.GetConsPubKeyBech32(pubkeyString) + if err5 != nil { + return fmt.Errorf(`Expected hex, base64, or bech32. Got errors: + hex: %v, + base64: %v + bech32 Acc: %v + bech32 Val: %v + bech32 Cons: %v`, + err, err2, err3, err4, err5) + } + + } + } + + } + } + + var pubKey ed25519.PubKeyEd25519 + if pubKeyI == nil { + copy(pubKey[:], pubkeyBytes) + } else { + pubKey = pubKeyI.(ed25519.PubKeyEd25519) + pubkeyBytes = pubKey[:] + } + + cdc := terra.MakeCodec() + pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey) + if err != nil { + return err + } + accPub, err := sdk.Bech32ifyAccPub(pubKey) + if err != nil { + return err + } + valPub, err := sdk.Bech32ifyValPub(pubKey) + if err != nil { + return err + } + + consenusPub, err := sdk.Bech32ifyConsPub(pubKey) + if err != nil { + return err + } + fmt.Println("Address:", pubKey.Address()) + fmt.Printf("Hex: %X\n", pubkeyBytes) + fmt.Println("JSON (base64):", string(pubKeyJSONBytes)) + fmt.Println("Bech32 Acc:", accPub) + fmt.Println("Bech32 Validator Operator:", valPub) + fmt.Println("Bech32 Validator Consensus:", consenusPub) + return nil +} + +func runAddrCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("Expected single arg") + } + + addrString := args[0] + var addr []byte + + // try hex, then bech32 + var err error + addr, err = hex.DecodeString(addrString) + if err != nil { + var err2 error + addr, err2 = sdk.AccAddressFromBech32(addrString) + if err2 != nil { + var err3 error + addr, err3 = sdk.ValAddressFromBech32(addrString) + + if err3 != nil { + return fmt.Errorf(`Expected hex or bech32. Got errors: + hex: %v, + bech32 acc: %v + bech32 val: %v + `, err, err2, err3) + + } + } + } + + accAddr := sdk.AccAddress(addr) + valAddr := sdk.ValAddress(addr) + + fmt.Println("Address:", addr) + fmt.Printf("Address (hex): %X\n", addr) + fmt.Printf("Bech32 Acc: %s\n", accAddr) + fmt.Printf("Bech32 Val: %s\n", valAddr) + return nil +} + +func runTxCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("Expected single arg") + } + + txString := args[0] + + // try hex, then base64 + txBytes, err := hex.DecodeString(txString) + if err != nil { + var err2 error + txBytes, err2 = base64.StdEncoding.DecodeString(txString) + if err2 != nil { + return fmt.Errorf(`Expected hex or base64. Got errors: + hex: %v, + base64: %v + `, err, err2) + } + } + + var tx = authtypes.StdTx{} + cdc := terra.MakeCodec() + + err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(tx) + if err != nil { + return err + } + + buf := bytes.NewBuffer([]byte{}) + err = json.Indent(buf, bz, "", " ") + if err != nil { + return err + } + + fmt.Println(buf.String()) + return nil +} + +func main() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } + os.Exit(0) +} diff --git a/cmd/terrakeyutil/main.go b/cmd/terrakeyutil/main.go deleted file mode 100644 index c5706dcf3..000000000 --- a/cmd/terrakeyutil/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "encoding/hex" - "fmt" - "os" - - "github.com/terra-project/core/types/util" - - "github.com/tendermint/tendermint/libs/bech32" -) - -var bech32Prefixes = []string{util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub, util.Bech32PrefixValAddr, util.Bech32PrefixValPub, util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub} - -func main() { - if len(os.Args) < 2 { - fmt.Println("Must specify an input string") - } - arg := os.Args[1] - runFromBech32(arg) - runFromHex(arg) -} - -// Print info from bech32. -func runFromBech32(bech32str string) { - hrp, bz, err := bech32.DecodeAndConvert(bech32str) - if err != nil { - fmt.Println("Not a valid bech32 string") - return - } - fmt.Println("Bech32 parse:") - fmt.Printf("Human readible part: %v\nBytes (hex): %X\n", - hrp, - bz, - ) -} - -func runFromHex(hexaddr string) { - bz, err := hex.DecodeString(hexaddr) - if err != nil { - fmt.Println("Not a valid hex string") - return - } - fmt.Println("Hex parse:") - fmt.Println("Bech32 formats:") - for _, prefix := range bech32Prefixes { - bech32Addr, err := bech32.ConvertAndEncode(prefix, bz) - if err != nil { - panic(err) - } - fmt.Println(" - " + bech32Addr) - } -} diff --git a/contrib/devtools/Makefile b/contrib/devtools/Makefile new file mode 100644 index 000000000..105024837 --- /dev/null +++ b/contrib/devtools/Makefile @@ -0,0 +1,76 @@ +### +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +### +ifeq ($(OS),Windows_NT) + GO := $(shell where go.exe 2> NUL) + FS := \\ +else + GO := $(shell command -v go 2> /dev/null) + FS := / +endif + +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif + +GOPATH ?= $(shell $(GO) env GOPATH) +GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com +GOLANGCI_LINT_VERSION := v1.17.1 +GOLANGCI_LINT_HASHSUM := f5fa647a12f658924d9f7d6b9628d505ab118e8e049e43272de6526053ebe08d + +### +# Functions +### + +go_get = $(if $(findstring Windows_NT,$(OS)),\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\ +,\ +mkdir -p $(GITHUBDIR)$(FS)$(1) &&\ +(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\ +)\ +cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) + +go_install = $(call go_get,$(1),$(2),$(3)) && cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && $(GO) install + +mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) +mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) + +### +# tools +### + +TOOLS_DESTDIR ?= $(GOPATH)/bin + +GOLANGCI_LINT = $(TOOLS_DESTDIR)/golangci-lint +GOIMPORTS = $(TOOLS_DESTDIR)/goimports +CLOG = $(TOOLS_DESTDIR)/clog +RUNSIM = $(TOOLS_DESTDIR)/runsim + +all: tools + +tools: tools-stamp +tools-stamp: $(GOIMPORTS) $(RUNSIM) + touch $@ + +$(GOLANGCI_LINT): $(mkfile_dir)/install-golangci-lint.sh + bash $(mkfile_dir)/install-golangci-lint.sh $(TOOLS_DESTDIR) $(GOLANGCI_LINT_VERSION) $(GOLANGCI_LINT_HASHSUM) + +$(GOIMPORTS): + go get golang.org/x/tools/cmd/goimports@v0.0.0-20190628034336-212fb13d595e + +$(CLOG): + go get github.com/cosmos/tools/cmd/clog/ + +$(RUNSIM): + go get github.com/cosmos/tools/cmd/runsim/ + +golangci-lint: $(GOLANGCI_LINT) + +tools-clean: + rm -f $(GOIMPORTS) $(CLOG) $(GOLANGCI_LINT) + rm -f tools-stamp + +.PHONY: all tools tools-clean diff --git a/contrib/devtools/install-golangci-lint.sh b/contrib/devtools/install-golangci-lint.sh new file mode 100644 index 000000000..b95713828 --- /dev/null +++ b/contrib/devtools/install-golangci-lint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -euo pipefail + +f_sha256() { + local l_file + l_file=$1 + python -sBc "import hashlib;print(hashlib.sha256(open('$l_file','rb').read()).hexdigest())" +} + +installer="$(mktemp)" +trap "rm -f ${installer}" EXIT + +GOBIN="${1}" +VERSION="${2}" +HASHSUM="${3}" +CURL="$(which curl)" + +echo "Downloading golangci-lint ${VERSION} installer ..." >&2 +"${CURL}" -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/${VERSION}/install.sh" > "${installer}" + +echo "Checking hashsum ..." >&2 +[ "${HASHSUM}" = "$(f_sha256 ${installer})" ] +chmod +x "${installer}" + +echo "Launching installer ..." >&2 +exec "${installer}" -d -b "${GOBIN}" "${VERSION}" diff --git a/contrib/export/columbus1-to-columbus2.py b/contrib/export/columbus1-to-columbus2.py deleted file mode 100644 index 2b34bb439..000000000 --- a/contrib/export/columbus1-to-columbus2.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 - -import lib -import sys -import dateutil.parser - -DECIMAL_UNIT = 3 -SECONDS_PER_MINUTE = 60 -SECONDS_PER_HOURS = SECONDS_PER_MINUTE * 60 -SECONDS_PER_DAY = SECONDS_PER_HOURS * 24 -SECONDS_PER_MONTH = SECONDS_PER_HOURS * 30 - -def process_raw_genesis(genesis, vesting_info, parsed_args): - - # parse genesis date - genesis_date = dateutil.parser.parse(genesis['genesis_time']) - - # modify i-4 type accounts vesting schedule - accounts = genesis['app_state']['accounts'] - - # vesting accouts who need to be updated - update_vesting_accounts = [] - for row in vesting_info: - update_vesting_accounts.append(row) - - for account in accounts: - for row in update_vesting_accounts: - if len(row) != 3: - sys.exit('invalid cvs format') - - if account['address'] == row[0]: - change_vesting_schedule(account, row[1], row[2], genesis_date) - - # Change wallet address of translink capital - if account['address'] == 'terra1d4v2k6h7ltc4mpkk9pgtp277yp2flte7296ypq': - account['address'] = 'terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6' - - - # Set new chain ID and genesis start time - genesis['chain_id'] = parsed_args.chain_id.strip() - genesis['genesis_time'] = parsed_args.start_time - - genesis['app_state']['market']['params']['daily_luna_delta_limit'] = "0.005" - genesis['app_state']['market']['params']['max_swap_spread'] = "0.1" - genesis['app_state']['market']['params']['min_swap_spread'] = "0.02" - - genesis['app_state']['oracle']['params']['vote_period'] = "12" - - genesis['app_state']['treasury']['params']['reward_policy']['rate_max'] = "0.9" - - return genesis - -def change_vesting_schedule(account, denom, amount, genesis_date): - - ratio = False - original_vesting = account['original_vesting'] - for vesting in original_vesting: - if vesting['denom'] == denom: - ratio = int(amount) / int(vesting['amount'][:-6]) - break - - if ratio == False: - sys.exit('invalid account or denom given') - - vesting_schedules = account['vesting_schedules'] - for vesting_schedule in vesting_schedules: - - if vesting_schedule['denom'] == denom: - - schedules = vesting_schedule['schedules'] - for schedule in schedules: - if schedule['cliff'] == str(get_time_after_n_month(genesis_date, 4)) or schedule['cliff'] == str(get_time_after_n_month(genesis_date, 5)): - schedule['ratio'] = str(float(schedule['ratio']) - (ratio / 2)) - - for i in range(4, 10): - cliff = get_time_after_n_month(genesis_date, i) - single_ratio = correct_decimal(ratio / 6) - - found = False - for schedule in schedules: - if schedule['cliff'] == str(cliff): - schedule['ratio'] = str(correct_decimal(float(schedule['ratio']) + single_ratio)) - found = True - break - - if not found: - schedules.append({ - 'cliff': str(cliff), - 'ratio': str(single_ratio) - }) - - schedules.sort(key=custom_sort) - - ratio_sum = 0 - for schedule in schedules: - ratio_sum += float(schedule['ratio']) - - last_index = len(schedules) - 1 - schedules[last_index]['ratio'] = str(round(float(schedules[last_index]['ratio']) - (ratio_sum - 1), DECIMAL_UNIT)) - - invariant_check(schedules) - -def custom_sort(schedule): - return schedule['cliff'] - -def correct_decimal(float_num): - return int(float_num * (10**DECIMAL_UNIT)) / (10**DECIMAL_UNIT) - -def invariant_check(schedules): - ratio_sum = 0 - for schedule in schedules: - ratio_sum += float(schedule['ratio']) - - if ratio_sum != 1: - sys.exit('%f, invariant failed' % ratio_sum) - - -def get_time_after_n_month(start_date, n): - target_month = start_date.month+n - year = target_month > 12 and start_date.year + int(target_month / 12) or start_date.year - month = target_month - 12 * (year - start_date.year) - return int(start_date.replace(year=year, month=month).timestamp()) - -if __name__ == '__main__': - parser = lib.init_default_argument_parser( - prog_desc='Convert genesis.json from columbus-1 to columbus-2', - default_chain_id='columbus-2', - default_start_time='2019-05-20T05:00:00Z', - ) - lib.main(parser, process_raw_genesis) diff --git a/contrib/export/i-4-vesting-type-accounts.csv b/contrib/export/i-4-vesting-type-accounts.csv deleted file mode 100644 index d0b3b4694..000000000 --- a/contrib/export/i-4-vesting-type-accounts.csv +++ /dev/null @@ -1,19 +0,0 @@ -terra1v5hrqlv8dqgzvy0pwzqzg0gxy899rm4kdn0jp4,uluna,12500 -terra19q6yfuyfn6aqurwerqee6v8zj5cmnk87azz39l,uluna,37500 -terra1wpplgwx5q2ph7z2vqm9m0t2jgr6qyjkwhxvff3,uluna,350000 -terra17fqhjf4xzn03ygdjaf8m7zmdeug09ttpexeuyv,uluna,187500 -terra1z5q2gc5fu00kwdamyux5kxt8sz744rptkmz8eq,uluna,1378571 -terra1usw5u2zacvp7rqvvryhwv8h3t2xukeswd8at2r,uluna,34286 -terra1je9e7mr5av3tvwwwfnmnalnu20lt2ngtk6qzcu,uluna,1244021 -terra1fs7mmpducjf25j70sk3sz6k5phz2fllmyr5gwz,uluna,2857143 -terra192w2pfqf5nlvqhaagl38k38xqmjargw9ynp9gn,uluna,187500 -terra1md2z3kpgr9dsdnklh796kh2lu09kfcjw7vk57n,uluna,293750 -terra1u037casar7s7xdzxd5wj6dlh2gszznryep654f,uluna,125000 -terra1an96a433dtlq5re387nnqe6psgy67rspgtw02e,uluna,1242857 -terra1j4yz326xk23p6g6hyj3ahffcxfpt3jfun092dk,uluna,200000 -terra1t5sez2hxs24p2ll8kkr4d6dxcjwmsv2m0fgsfc,uluna,20000 -terra13th6kl3ynvnz96jr5t7k63p29tuaftsvre5s5l,uluna,177143 -terra12lsxcykm4d784n2ee974khr7u7asqzk8twyvv2,uluna,362500 -terra1ugnddychay5ja5tqdx86z4r75c3jm69ycjv6eh,uluna,407143 -terra186ym2wg09yxvneu4armqp4ceal8qake2d0lmal,uluna,1250000 -terra1f9wjf3taxmq2srsxhrna456fz98nllkeaw4sgr,uluna,605714 \ No newline at end of file diff --git a/contrib/export/lib.py b/contrib/export/lib.py deleted file mode 100644 index f8071a7ce..000000000 --- a/contrib/export/lib.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import json -import sys -import csv - - -def init_default_argument_parser(prog_desc, default_chain_id, default_start_time): - parser = argparse.ArgumentParser(description=prog_desc) - parser.add_argument( - 'exported_genesis', - help='exported genesis.json file', - type=argparse.FileType('r'), default=sys.stdin, - ) - parser.add_argument( - 'vesting_info', - help='i-4-vesting-type-accounts.csv file', - type=argparse.FileType('r'), default=sys.stdin, - ) - parser.add_argument('--chain-id', type=str, default=default_chain_id) - parser.add_argument('--start-time', type=str, default=default_start_time) - return parser - - -def main(argument_parser, process_genesis_func): - args = argument_parser.parse_args() - if args.chain_id.strip() == '': - sys.exit('chain-id required') - - genesis = json.loads(args.exported_genesis.read()) - vesting_info = csv.reader(args.vesting_info) - - print(json.dumps(process_genesis_func( - genesis=genesis, vesting_info=vesting_info, parsed_args=args,), indent=True)) \ No newline at end of file diff --git a/contrib/get_node.sh b/contrib/get_node.sh new file mode 100755 index 000000000..7f0dd6e38 --- /dev/null +++ b/contrib/get_node.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +VERSION=v11.15.0 +NODE_FULL=node-${VERSION}-linux-x64 + +mkdir -p ~/.local/bin +mkdir -p ~/.local/node +wget http://nodejs.org/dist/${VERSION}/${NODE_FULL}.tar.gz -O ~/.local/node/${NODE_FULL}.tar.gz +tar -xzf ~/.local/node/${NODE_FULL}.tar.gz -C ~/.local/node/ +ln -s ~/.local/node/${NODE_FULL}/bin/node ~/.local/bin/node +ln -s ~/.local/node/${NODE_FULL}/bin/npm ~/.local/bin/npm +export PATH=~/.local/bin:$PATH +npm i -g dredd@11.0.1 +ln -s ~/.local/node/${NODE_FULL}/bin/dredd ~/.local/bin/dredd diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh new file mode 100755 index 000000000..de5166cde --- /dev/null +++ b/contrib/gitian-build.sh @@ -0,0 +1,201 @@ +#!/bin/bash + +# symbol prefixes: +# g_ -> global +# l_ - local variable +# f_ -> function + +set -euo pipefail + +GITIAN_CACHE_DIRNAME='.gitian-builder-cache' +GO_DEBIAN_RELEASE='1.12.7-1' +GO_TARBALL="golang-debian-${GO_DEBIAN_RELEASE}.tar.gz" +GO_TARBALL_URL="https://salsa.debian.org/go-team/compiler/golang/-/archive/debian/${GO_DEBIAN_RELEASE}/${GO_TARBALL}" + +# Defaults + +DEFAULT_SIGN_COMMAND='gpg --detach-sign' +DEFAULT_TERRA_SIGS=${TERRA_SIGS:-'terra.sigs'} +DEFAULT_GITIAN_REPO='https://github.com/devrandom/gitian-builder' +DEFAULT_GBUILD_FLAGS='' +DEFAULT_SIGS_REPO='https://github.com/cosmos/terra.sigs' + +# Overrides + +SIGN_COMMAND=${SIGN_COMMAND:-${DEFAULT_SIGN_COMMAND}} +GITIAN_REPO=${GITIAN_REPO:-${DEFAULT_GITIAN_REPO}} +GBUILD_FLAGS=${GBUILD_FLAGS:-${DEFAULT_GBUILD_FLAGS}} + +# Globals + +g_workdir='' +g_gitian_cache='' +g_cached_gitian='' +g_cached_go_tarball='' +g_sign_identity='' +g_sigs_dir='' +g_flag_commit='' + + +f_help() { + cat >&2 <&2 + mkdir "${l_builddir}/inputs/" + cp -v "${g_cached_go_tarball}" "${l_builddir}/inputs/" + done +} + +f_build() { + local l_descriptor + + l_descriptor=$1 + + bin/gbuild --commit terra="$g_commit" ${GBUILD_FLAGS} "$l_descriptor" + libexec/stop-target || f_echo_stderr "warning: couldn't stop target" +} + +f_sign_verify() { + local l_descriptor + + l_descriptor=$1 + + bin/gsign -p "${SIGN_COMMAND}" -s "${g_sign_identity}" --destination="${g_sigs_dir}" --release=${g_release} ${l_descriptor} + bin/gverify --destination="${g_sigs_dir}" --release="${g_release}" ${l_descriptor} +} + +f_commit_sig() { + local l_release_name + + l_release_name=$1 + + pushd "${g_sigs_dir}" + git add . || echo "git add failed" >&2 + git commit -m "Add ${l_release_name} reproducible build" || echo "git commit failed" >&2 + popd +} + +f_prep_docker_image() { + pushd $1 + bin/make-base-vm --docker --suite bionic --arch amd64 + popd +} + +f_ensure_cache() { + g_gitian_cache="${g_workdir}/${GITIAN_CACHE_DIRNAME}" + [ -d "${g_gitian_cache}" ] || mkdir "${g_gitian_cache}" + + g_cached_go_tarball="${g_gitian_cache}/${GO_TARBALL}" + if [ ! -f "${g_cached_go_tarball}" ]; then + f_echo_stderr "${g_cached_go_tarball}: cache miss, caching..." + curl -L "${GO_TARBALL_URL}" --output "${g_cached_go_tarball}" + fi + + g_cached_gitian="${g_gitian_cache}/gitian-builder" + if [ ! -d "${g_cached_gitian}" ]; then + f_echo_stderr "${g_cached_gitian}: cache miss, caching..." + git clone ${GITIAN_REPO} "${g_cached_gitian}" + fi +} + +f_demangle_platforms() { + case "${1}" in + all) + printf '%s' 'darwin linux windows' ;; + linux|darwin|windows) + printf '%s' "${1}" ;; + *) + echo "invalid platform -- ${1}" + exit 1 + esac +} + +f_echo_stderr() { + echo $@ >&2 +} + + +while getopts ":cs:h" opt; do + case "${opt}" in + h) f_help ; exit 0 ;; + c) g_flag_commit=y ;; + s) g_sign_identity="${OPTARG}" ;; + esac +done + +shift "$((OPTIND-1))" + +g_platforms=$(f_demangle_platforms "${1}") +g_workdir="$(pwd)" +g_commit="$(git rev-parse HEAD)" +g_sigs_dir=${TERRA_SIGS:-"${g_workdir}/${DEFAULT_TERRA_SIGS}"} + +f_ensure_cache + +f_prep_docker_image "${g_cached_gitian}" + +f_prep_build "${g_platforms}" + +export USE_DOCKER=1 +for g_os in ${g_platforms}; do + g_release="$(git describe --tags --abbrev=9 | sed 's/^v//')-${g_os}" + g_descriptor="${g_workdir}/contrib/gitian-descriptors/gitian-${g_os}.yml" + [ -f ${g_descriptor} ] + g_builddir="$(f_builddir ${g_os})" + + pushd "${g_builddir}" + f_build "${g_descriptor}" + if [ -n "${g_sign_identity}" ]; then + f_sign_verify "${g_descriptor}" + fi + popd + + if [ -n "${g_sign_identity}" -a -n "${g_flag_commit}" ]; then + [ -d "${g_sigs_dir}/.git/" ] && f_commit_sig ${g_release} || f_echo_stderr "couldn't commit, ${g_sigs_dir} is not a git clone" + fi +done + +exit 0 diff --git a/contrib/gitian-descriptors/gitian-darwin.yml b/contrib/gitian-descriptors/gitian-darwin.yml new file mode 100644 index 000000000..126f71cc2 --- /dev/null +++ b/contrib/gitian-descriptors/gitian-darwin.yml @@ -0,0 +1,118 @@ +--- +name: "terra-darwin" +enable_cache: true +distro: "ubuntu" +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "bsdmainutils" +- "build-essential" +- "ca-certificates" +- "curl" +- "debhelper" +- "dpkg-dev" +- "devscripts" +- "fakeroot" +- "git" +- "golang-any" +- "xxd" +- "quilt" +remotes: +- "url": "https://github.com/cosmos/terra.git" + "dir": "terra" +files: +- "golang-debian-1.12.7-1.tar.gz" +script: | + set -e -o pipefail + + GO_SRC_RELEASE=golang-debian-1.12.7-1 + GO_SRC_TARBALL="${GO_SRC_RELEASE}.tar.gz" + # Compile go and configure the environment + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export BUILD_DIR=`pwd` + tar xf "${GO_SRC_TARBALL}" + rm -f "${GO_SRC_TARBALL}" + [ -d "${GO_SRC_RELEASE}/" ] + mv "${GO_SRC_RELEASE}/" go/ + pushd go/ + QUILT_PATCHES=debian/patches quilt push -a + fakeroot debian/rules build RUN_TESTS=false GOCACHE=/tmp/go-cache + popd + + export GOOS=darwin + export GOROOT=${BUILD_DIR}/go + export GOPATH=${BUILD_DIR}/gopath + mkdir -p ${GOPATH}/bin + + export PATH_orig=${PATH} + export PATH=$GOPATH/bin:$GOROOT/bin:$PATH + + export ARCHS='386 amd64' + export GO111MODULE=on + + # Make release tarball + pushd terra + VERSION=$(git describe --tags | sed 's/^v//') + COMMIT=$(git log -1 --format='%H') + DISTNAME=terra-${VERSION} + git archive --format tar.gz --prefix ${DISTNAME}/ -o ${DISTNAME}.tar.gz HEAD + SOURCEDIST=`pwd`/`echo terra-*.tar.gz` + popd + + # Correct tar file order + mkdir -p temp + pushd temp + tar xf $SOURCEDIST + rm $SOURCEDIST + find terra-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > $SOURCEDIST + popd + + # Prepare GOPATH and install deps + distsrc=${GOPATH}/src/github.com/cosmos/terra + mkdir -p ${distsrc} + pushd ${distsrc} + tar --strip-components=1 -xf $SOURCEDIST + go mod download + popd + + # Configure LDFLAGS for reproducible builds + LDFLAGS="-extldflags=-static -buildid=${VERSION} -s -w \ + -X github.com/cosmos/cosmos-sdk/version.Name=terra \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=terrad \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=terracli \ + -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} \ + -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} \ + -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger" + + # Extract release tarball and build + for arch in ${ARCHS}; do + INSTALLPATH=`pwd`/installed/${DISTNAME}-${arch} + mkdir -p ${INSTALLPATH} + + # Build terra tool suite + pushd ${distsrc} + for prog in terracli terrad; do + GOARCH=${arch} GOROOT_FINAL=${GOROOT} go build -a \ + -gcflags=all=-trimpath=${GOPATH} \ + -asmflags=all=-trimpath=${GOPATH} \ + -mod=readonly -tags "netgo ledger" \ + -ldflags="${LDFLAGS}" \ + -o ${INSTALLPATH}/${prog} ./cmd/${prog} + + done + popd # ${distsrc} + + pushd ${INSTALLPATH} + find -type f | sort | tar \ + --no-recursion --mode='u+rw,go+r-w,a+X' \ + --numeric-owner --sort=name \ + --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-darwin-${arch}.tar.gz + popd # installed + done + + rm -rf ${distsrc} + + mkdir -p $OUTDIR/src + mv $SOURCEDIST $OUTDIR/src diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml new file mode 100644 index 000000000..583d0f843 --- /dev/null +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -0,0 +1,117 @@ +--- +name: "terra-linux" +enable_cache: true +distro: "ubuntu" +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "bsdmainutils" +- "build-essential" +- "ca-certificates" +- "curl" +- "debhelper" +- "dpkg-dev" +- "devscripts" +- "fakeroot" +- "git" +- "golang-any" +- "xxd" +- "quilt" +remotes: +- "url": "https://github.com/cosmos/terra.git" + "dir": "terra" +files: +- "golang-debian-1.12.7-1.tar.gz" +script: | + set -e -o pipefail + + GO_SRC_RELEASE=golang-debian-1.12.7-1 + GO_SRC_TARBALL="${GO_SRC_RELEASE}.tar.gz" + # Compile go and configure the environment + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export BUILD_DIR=`pwd` + tar xf "${GO_SRC_TARBALL}" + rm -f "${GO_SRC_TARBALL}" + [ -d "${GO_SRC_RELEASE}/" ] + mv "${GO_SRC_RELEASE}/" go/ + pushd go/ + QUILT_PATCHES=debian/patches quilt push -a + fakeroot debian/rules build RUN_TESTS=false GOCACHE=/tmp/go-cache + popd + + export GOROOT=${BUILD_DIR}/go + export GOPATH=${BUILD_DIR}/gopath + mkdir -p ${GOPATH}/bin + + export PATH_orig=${PATH} + export PATH=$GOPATH/bin:$GOROOT/bin:$PATH + + export ARCHS='386 amd64 arm arm64' + export GO111MODULE=on + + # Make release tarball + pushd terra + VERSION=$(git describe --tags | sed 's/^v//') + COMMIT=$(git log -1 --format='%H') + DISTNAME=terra-${VERSION} + git archive --format tar.gz --prefix ${DISTNAME}/ -o ${DISTNAME}.tar.gz HEAD + SOURCEDIST=`pwd`/`echo terra-*.tar.gz` + popd + + # Correct tar file order + mkdir -p temp + pushd temp + tar xf $SOURCEDIST + rm $SOURCEDIST + find terra-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > $SOURCEDIST + popd + + # Prepare GOPATH and install deps + distsrc=${GOPATH}/src/github.com/cosmos/terra + mkdir -p ${distsrc} + pushd ${distsrc} + tar --strip-components=1 -xf $SOURCEDIST + go mod download + popd + + # Configure LDFLAGS for reproducible builds + LDFLAGS="-extldflags=-static -buildid=${VERSION} -s -w \ + -X github.com/cosmos/cosmos-sdk/version.Name=terra \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=terrad \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=terracli \ + -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} \ + -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} \ + -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger" + + # Extract release tarball and build + for arch in ${ARCHS}; do + INSTALLPATH=`pwd`/installed/${DISTNAME}-${arch} + mkdir -p ${INSTALLPATH} + + # Build terra tool suite + pushd ${distsrc} + for prog in terracli terrad; do + GOARCH=${arch} GOROOT_FINAL=${GOROOT} go build -a \ + -gcflags=all=-trimpath=${GOPATH} \ + -asmflags=all=-trimpath=${GOPATH} \ + -mod=readonly -tags "netgo ledger" \ + -ldflags="${LDFLAGS}" \ + -o ${INSTALLPATH}/${prog} ./cmd/${prog} + + done + popd # ${distsrc} + + pushd ${INSTALLPATH} + find -type f | sort | tar \ + --no-recursion --mode='u+rw,go+r-w,a+X' \ + --numeric-owner --sort=name \ + --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-linux-${arch}.tar.gz + popd # installed + done + + rm -rf ${distsrc} + + mkdir -p $OUTDIR/src + mv $SOURCEDIST $OUTDIR/src diff --git a/contrib/gitian-descriptors/gitian-windows.yml b/contrib/gitian-descriptors/gitian-windows.yml new file mode 100644 index 000000000..201a6fe6e --- /dev/null +++ b/contrib/gitian-descriptors/gitian-windows.yml @@ -0,0 +1,119 @@ +--- +name: "terra-windows" +enable_cache: true +distro: "ubuntu" +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "bsdmainutils" +- "build-essential" +- "ca-certificates" +- "curl" +- "debhelper" +- "dpkg-dev" +- "devscripts" +- "fakeroot" +- "git" +- "golang-any" +- "xxd" +- "quilt" +remotes: +- "url": "https://github.com/cosmos/terra.git" + "dir": "terra" +files: +- "golang-debian-1.12.7-1.tar.gz" +script: | + set -e -o pipefail + + GO_SRC_RELEASE=golang-debian-1.12.7-1 + GO_SRC_TARBALL="${GO_SRC_RELEASE}.tar.gz" + # Compile go and configure the environment + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export BUILD_DIR=`pwd` + tar xf "${GO_SRC_TARBALL}" + rm -f "${GO_SRC_TARBALL}" + [ -d "${GO_SRC_RELEASE}/" ] + mv "${GO_SRC_RELEASE}/" go/ + pushd go/ + QUILT_PATCHES=debian/patches quilt push -a + fakeroot debian/rules build RUN_TESTS=false GOCACHE=/tmp/go-cache + popd + + export GOOS=windows + export GOROOT=${BUILD_DIR}/go + export GOPATH=${BUILD_DIR}/gopath + mkdir -p ${GOPATH}/bin + + export PATH_orig=${PATH} + export PATH=$GOPATH/bin:$GOROOT/bin:$PATH + + export ARCHS='386 amd64' + export GO111MODULE=on + + # Make release tarball + pushd terra + VERSION=$(git describe --tags | sed 's/^v//') + COMMIT=$(git log -1 --format='%H') + DISTNAME=terra-${VERSION} + git archive --format tar.gz --prefix ${DISTNAME}/ -o ${DISTNAME}.tar.gz HEAD + SOURCEDIST=`pwd`/`echo terra-*.tar.gz` + popd + + # Correct tar file order + mkdir -p temp + pushd temp + tar xf $SOURCEDIST + rm $SOURCEDIST + find terra-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > $SOURCEDIST + popd + + # Prepare GOPATH and install deps + distsrc=${GOPATH}/src/github.com/cosmos/terra + mkdir -p ${distsrc} + pushd ${distsrc} + tar --strip-components=1 -xf $SOURCEDIST + go mod download + popd + + # Configure LDFLAGS for reproducible builds + LDFLAGS="-extldflags=-static -buildid=${VERSION} -s -w \ + -X github.com/cosmos/cosmos-sdk/version.Name=terra \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=terrad \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=terracli \ + -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} \ + -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} \ + -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger" + + # Extract release tarball and build + for arch in ${ARCHS}; do + INSTALLPATH=`pwd`/installed/${DISTNAME}-${arch} + mkdir -p ${INSTALLPATH} + + # Build terra tool suite + pushd ${distsrc} + for prog in terracli terrad; do + exe=${prog}.exe + GOARCH=${arch} GOROOT_FINAL=${GOROOT} go build -a \ + -gcflags=all=-trimpath=${GOPATH} \ + -asmflags=all=-trimpath=${GOPATH} \ + -mod=readonly -tags "netgo ledger" \ + -ldflags="${LDFLAGS}" \ + -o ${INSTALLPATH}/${exe} ./cmd/${prog} + + done + popd # ${distsrc} + + pushd ${INSTALLPATH} + find -type f | sort | tar \ + --no-recursion --mode='u+rw,go+r-w,a+X' \ + --numeric-owner --sort=name \ + --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-windows-${arch}.tar.gz + popd # installed + done + + rm -rf ${distsrc} + + mkdir -p $OUTDIR/src + mv $SOURCEDIST $OUTDIR/src diff --git a/contrib/gitian-keys/README.md b/contrib/gitian-keys/README.md new file mode 100644 index 000000000..110e83df1 --- /dev/null +++ b/contrib/gitian-keys/README.md @@ -0,0 +1,29 @@ +## PGP keys of Gitian builders and Terra Developers + +The file `keys.txt` contains fingerprints of the public keys of Gitian builders +and active developers. + +The associated keys are mainly used to sign git commits or the build results +of Gitian builds. + +The most recent version of each pgp key can be found on most PGP key servers. + +Fetch the latest version from the key server to see if any key was revoked in +the meantime. +To fetch the latest version of all pgp keys in your gpg homedir, + +```sh +gpg --refresh-keys +``` + +To fetch keys of Gitian builders and active core developers, feed the list of +fingerprints of the primary keys into gpg: + +```sh +while read fingerprint keyholder_name; \ +do gpg --keyserver hkp://subset.pool.sks-keyservers.net \ +--recv-keys ${fingerprint}; done < ./keys.txt +``` + +Add your key to the list if you are a Terra core developer or you have +provided Gitian signatures for two major or minor releases of Terra. diff --git a/contrib/gitian-keys/keys.txt b/contrib/gitian-keys/keys.txt new file mode 100644 index 000000000..1d9cf6ec5 --- /dev/null +++ b/contrib/gitian-keys/keys.txt @@ -0,0 +1,2 @@ +04160004A8276E40BB9890FBE8A48AE5311D765A Alessio Treglia +237396563D09DCD65B122AE7EC1904F1389EF7E5 Karoly Albert Szabo diff --git a/contrib/localnet-blocks-test.sh b/contrib/localnet-blocks-test.sh new file mode 100755 index 000000000..53df090ff --- /dev/null +++ b/contrib/localnet-blocks-test.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +CNT=0 +ITER=$1 +SLEEP=$2 +NUMBLOCKS=$3 +NODEADDR=$4 + +if [ -z "$1" ]; then + echo "Need to input number of iterations to run..." + exit 1 +fi + +if [ -z "$2" ]; then + echo "Need to input number of seconds to sleep between iterations" + exit 1 +fi + +if [ -z "$3" ]; then + echo "Need to input block height to declare completion..." + exit 1 +fi + +if [ -z "$4" ]; then + echo "Need to input node address to poll..." + exit 1 +fi + +while [ ${CNT} -lt $ITER ]; do + var=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height') + echo "Number of Blocks: ${var}" + if [ ! -z ${var} ] && [ ${var} -gt ${NUMBLOCKS} ]; then + echo "Number of blocks reached, exiting success..." + exit 0 + fi + let CNT=CNT+1 + sleep $SLEEP +done + +echo "Timeout reached, exiting failure..." +exit 1 diff --git a/docs/guide/terracli.md b/docs/guide/terracli.md index 00a021b26..aa7292373 100644 --- a/docs/guide/terracli.md +++ b/docs/guide/terracli.md @@ -543,7 +543,7 @@ terracli keys show p1p2p3 --show-multisig The first step to create a multisig transaction is to initiate it on behalf of the multisig address created above: ```bash -terracli tx send terra1570v2fq3twt0f0x02vhxpuzc9jc4yl30q2qned 10stake \ +terracli tx send terra1570v2fq3twt0f0x02vhxpuzc9jc4yl30q2qned 10uluna \ --from= \ --generate-only > unsignedTx.json ``` diff --git a/go.mod b/go.mod index 2682d8a9c..984757f75 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,19 @@ module github.com/terra-project/core go 1.12 require ( - github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d - github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c // indirect - github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38 // indirect - github.com/cosmos/cosmos-sdk v0.0.0-00010101000000-000000000000 - github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d - github.com/golangci/golangci-lint v1.17.1 // indirect - github.com/gorilla/mux v1.7.0 + github.com/cosmos/cosmos-sdk v0.37.0 + github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect + github.com/gorilla/mux v1.7.2 + github.com/otiai10/copy v1.0.1 + github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776 // indirect github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v0.9.3 // indirect github.com/rakyll/statik v0.1.6 - github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect - github.com/spf13/cobra v0.0.3 + github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa + github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 github.com/spf13/viper v1.3.2 github.com/stretchr/testify v1.3.0 - github.com/syndtr/goleveldb v1.0.0 // indirect - github.com/tendermint/go-amino v0.14.1 - github.com/tendermint/tendermint v0.31.5 - google.golang.org/grpc v1.21.0 // indirect + github.com/tendermint/go-amino v0.15.0 + github.com/tendermint/tendermint v0.32.2 + github.com/tendermint/tm-db v0.1.1 ) - -replace github.com/cosmos/cosmos-sdk => github.com/YunSuk-Yeo/cosmos-sdk v0.35.2-terra - -replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 diff --git a/go.sum b/go.sum index 7075fe44a..94051b2ce 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,7 @@ bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2 h1:HTOmFEEYrWi4MW5ZKUx6xfeyM10Sx3kQF65xiQJMPYA= -github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/YunSuk-Yeo/cosmos-sdk v0.34.7-terra h1:SD0ufmYj1CptVhHTNiErt4riLahOmU3YqyFQuYAM9mQ= -github.com/YunSuk-Yeo/cosmos-sdk v0.34.7-terra/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.0-terra h1:IvNkhC53iJpUruq8ErQv13l9JD/cPX/WXKTWHZSPl30= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.0-terra/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.1-terra h1:Xwjfh2FY54ptyIBPs4SA1Rk4uwGj6eavs9KkwbtcegQ= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.1-terra/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.2-terra h1:8Vf1FfsRTn7mo6DryoE0ldhenhGzjXtjuf3aMpKwyA0= -github.com/YunSuk-Yeo/cosmos-sdk v0.35.2-terra/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -24,31 +10,24 @@ github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4 github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c h1:5N/b57wo2KfeHCGGdcXtOPsHqkPD+veLZhK/bMg2anQ= -github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38 h1:GbQHMJ2u/geMPV1tbN7i7zARSoPAPuXWa44V0KYvJXU= -github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/cosmos-sdk v0.37.0 h1:S2I3NDGN2wqfGlY5KqkAHTpfezjhgeqDxrCxhlhd528= +github.com/cosmos/cosmos-sdk v0.37.0/go.mod h1:3b/k/Zd+YDuttSmEJdNkxga1H5EIiDUhSYeErAHQN7A= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -56,174 +35,78 @@ github.com/cosmos/ledger-cosmos-go v0.10.3 h1:Qhi5yTR5Pg1CaTpd00pxlGwNl4sFRdtK1J github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fatih/color v1.6.0 h1:66qjqZk8kalYAvDRtM1AdAJQI0tj4Wrue3Eq3B3pmFU= -github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540 h1:7CU1IXBpPvxpQ/NqJrpuMXMHAw+FB2vfqtRF8tgW9fw= -github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= +github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= -github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 h1:i2jIkQFb8RG45DuQs+ElyROY848cSJIoIkBM+7XXypA= -github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196 h1:9rtVlONXLF1rJZzvLt4tfOXtnAFUEhxCJ64Ibzj6ECo= -github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98 h1:ir6/L2ZOJfFrJlOTsuf/hlzdPuUwXV/VzkSlgS6f1vs= -github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.17.1 h1:lc8Hf9GPCjIr0hg3S/xhvFT1+Hydass8F1xchr8jkME= -github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g= -github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547 h1:qMomh8bv+kDazm1dSLZ9S3zZ2PJZMHL4ilfBjxFOlmI= -github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc h1:XRFao922N8F3EcIXBSNX8Iywk+GI0dxD/8FicMX2D/c= -github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217 h1:r7vyX+SN24x6+5AnpnrRn/bdwBb7U+McZqCHOVtXDuk= -github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/libp2p/go-buffer-pool v0.0.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1/go.mod h1:pXzZSDlN+HPzSdyIBnKNN9ptD9Hx7iZMWIJPTwo4FPE= +github.com/otiai10/copy v1.0.1 h1:gtBjD8aq4nychvRZ2CyJvFWAw0aja+VHazDdruZKGZA= +github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4= github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -234,69 +117,42 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa h1:YJfZp12Z3AFhSBeXOlv4BO55RMwPn2NoQeDsrdWnBtY= +github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -304,103 +160,73 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= -github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 h1:u8i49c+BxloX3XQ55cvzFNXplizZP/q00i+IlttUjAU= github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= -github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -github.com/tendermint/iavl v0.12.1 h1:JDfyhM/Hhrumu1CL1Nxrypm8sNTPYqmeHo1IZLiJoXM= -github.com/tendermint/iavl v0.12.1/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM= -github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4= -github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc= -github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb h1:lI9ufgFfvuqRctP9Ny8lDDLbSWCMxBPletcSqrnyFYM= -github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk= +github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8= +github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o= +github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= +github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXHwyrV1QY= +github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE= +github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34 h1:B1LAOfRqg2QUyCdzfjf46quTSYUTAK5OCwbh6pljHbM= -mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/networks/Makefile b/networks/Makefile index 65b6a5b22..3b722b66c 100644 --- a/networks/Makefile +++ b/networks/Makefile @@ -26,7 +26,7 @@ CONFIGFILE?=$(CURDIR)/../build/config.toml # Name of application for app deployments APP_NAME ?= faucettestnet1 # Region to deploy VPC and application in AWS -REGION ?= ap-northeast-2 +REGION ?= us-east-2 all: @echo "There is no all. Only sum of the ones." @@ -59,7 +59,7 @@ validators-start: disclaimer @if ! [ -f $(HOME)/.ssh/id_rsa.pub ]; then ssh-keygen ; fi @if [ -z "`file $(BINARY) | grep 'ELF 64-bit'`" ]; then echo "Please build a linux binary using 'make build-linux'." ; false ; fi cd remote/terraform-aws && terraform init && (terraform workspace new "$(CLUSTER_NAME)" || terraform workspace select "$(CLUSTER_NAME)") && terraform apply -auto-approve -var SSH_PUBLIC_FILE="$(HOME)/.ssh/id_rsa.pub" -var SSH_PRIVATE_FILE="$(HOME)/.ssh/id_rsa" -var TESTNET_NAME="$(CLUSTER_NAME)" -var SERVERS="$(SERVERS)" -var REGION_LIMIT="$(REGION_LIMIT)" - cd remote/ansible && ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory/ec2.py -l "tag_Environment_$(CLUSTER_NAME)" -u centos -b -e BINARY=$(BINARY) -e TESTNET_NAME="$(TESTNET_NAME)" -e FAUCET_ADDRESS="$(FAUCET_ADDRESS)" -e FAUCET_COINS="$(FAUCET_COINS)" setup-validators.yml + cd remote/ansible && ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory/ec2.py -l "tag_Environment_$(CLUSTER_NAME)" -u centos -b -e BINARY=$(BINARY) -e TESTNET_NAME="$(TESTNET_NAME)" setup-validators.yml cd remote/ansible && ansible-playbook -i inventory/ec2.py -l "tag_Environment_$(CLUSTER_NAME)" -u centos -b start.yml validators-stop: disclaimer diff --git a/networks/README.md b/networks/README.md index 221f6e385..fbad4981c 100644 --- a/networks/README.md +++ b/networks/README.md @@ -3,4 +3,4 @@ Here contains the files required for automated deployment of either local or remote testnets. Doing so is best accomplished using the `make` targets. For more information, see the -[networks documentation](/docs/getting-started/networks.md) +[networks documentation](/docs/terra/networks.md) diff --git a/networks/local/terradnode/Dockerfile b/networks/local/terradnode/Dockerfile index 5168b54b9..d891f8db4 100644 --- a/networks/local/terradnode/Dockerfile +++ b/networks/local/terradnode/Dockerfile @@ -1,11 +1,11 @@ FROM alpine:3.7 -MAINTAINER Greg Szabo +MAINTAINER YunSuk Yeo RUN apk update && \ apk upgrade && \ apk --no-cache add curl jq file -VOLUME /terrad +VOLUME [ /terrad ] WORKDIR /terrad EXPOSE 26656 26657 ENTRYPOINT ["/usr/bin/wrapper.sh"] diff --git a/networks/remote/ansible/roles/add-lcd/templates/terracli.service.j2 b/networks/remote/ansible/roles/add-lcd/templates/gaiacli.service.j2 similarity index 100% rename from networks/remote/ansible/roles/add-lcd/templates/terracli.service.j2 rename to networks/remote/ansible/roles/add-lcd/templates/gaiacli.service.j2 diff --git a/networks/remote/ansible/roles/extract-config/tasks/main.yml b/networks/remote/ansible/roles/extract-config/tasks/main.yml index fdcf8aaef..94340b62c 100644 --- a/networks/remote/ansible/roles/extract-config/tasks/main.yml +++ b/networks/remote/ansible/roles/extract-config/tasks/main.yml @@ -12,9 +12,3 @@ become: yes become_user: terrad -- name: Fetch priv_validator_state.json - fetch: "src=/home/terrad/.terrad/data/priv_validator_state.json dest={{CONFIGFILE}} flat=yes" - run_once: yes - become: yes - become_user: terrad - diff --git a/networks/remote/ansible/roles/set-debug/files/sysconfig/terracli b/networks/remote/ansible/roles/set-debug/files/sysconfig/gaiacli similarity index 100% rename from networks/remote/ansible/roles/set-debug/files/sysconfig/terracli rename to networks/remote/ansible/roles/set-debug/files/sysconfig/gaiacli diff --git a/networks/remote/ansible/roles/set-debug/files/sysconfig/terrad b/networks/remote/ansible/roles/set-debug/files/sysconfig/gaiad similarity index 100% rename from networks/remote/ansible/roles/set-debug/files/sysconfig/terrad rename to networks/remote/ansible/roles/set-debug/files/sysconfig/gaiad diff --git a/networks/remote/ansible/roles/setup-fullnodes/files/terrad.service b/networks/remote/ansible/roles/setup-fullnodes/files/gaiad.service similarity index 100% rename from networks/remote/ansible/roles/setup-fullnodes/files/terrad.service rename to networks/remote/ansible/roles/setup-fullnodes/files/gaiad.service diff --git a/networks/remote/ansible/roles/setup-validators/files/terrad.service b/networks/remote/ansible/roles/setup-validators/files/gaiad.service similarity index 83% rename from networks/remote/ansible/roles/setup-validators/files/terrad.service rename to networks/remote/ansible/roles/setup-validators/files/gaiad.service index 24fa5104f..2ebf06f6e 100644 --- a/networks/remote/ansible/roles/setup-validators/files/terrad.service +++ b/networks/remote/ansible/roles/setup-validators/files/gaiad.service @@ -11,8 +11,6 @@ PermissionsStartOnly=true ExecStart=/usr/bin/terrad start ExecReload=/bin/kill -HUP $MAINPID KillSignal=SIGTERM -StandardOutput=syslog+console -StandardError=syslog+console [Install] WantedBy=multi-user.target diff --git a/networks/remote/ansible/roles/setup-validators/tasks/main.yml b/networks/remote/ansible/roles/setup-validators/tasks/main.yml index 467485dd0..816151ace 100644 --- a/networks/remote/ansible/roles/setup-validators/tasks/main.yml +++ b/networks/remote/ansible/roles/setup-validators/tasks/main.yml @@ -24,53 +24,55 @@ changed_when: false register: nodeid -- name: Make node infomation - command: "echo node{{nodeid.stdout}}@{{inventory_hostname}}" - register: nodeinfo - -- name: debug dump of node informations - run_once: true - debug: - msg: "{{ groups[ansible_limit] | map('extract', hostvars, ['nodeinfo', 'stdout']) | join(',') }}" +- name: Create initial transaction + command: "/usr/bin/terrad init gen-tx --name=node{{nodeid.stdout_lines[0]}} --ip={{inventory_hostname}}" + register: gentxresult + become: yes + become_user: terrad + args: + creates: /home/terrad/.terrad/config/gentx -- name: Create remote testnet configurations - command: "{{BINARY}} testnet --output-dir={{playbook_dir}}/testnets/{{TESTNET_NAME}} --faucet={{FAUCET_ADDRESS}} --faucet-coins={{FAUCET_COINS}} --chain-id={{TESTNET_NAME}} --predefined-nodes=\"{{ groups[ansible_limit] | map('extract', hostvars, ['nodeinfo', 'stdout']) | join(',') }}\"" +- name: Get wallet word seed from result of initial transaction locally + when: gentxresult["changed"] + shell: "echo '{{gentxresult.stdout}}' | python -c 'import json,sys ; print json.loads(\"\".join(sys.stdin.readlines()))[\"app_message\"][\"secret\"]'" + changed_when: false + register: walletkey connection: local + +- name: Write wallet word seed to local files + when: gentxresult["changed"] + copy: "content={{walletkey.stdout}} dest=keys/node{{nodeid.stdout_lines[0]}}" become: no - run_once: true - args: - creates: "{{playbook_dir}}/testnets/{{TESTNET_NAME}}" + connection: local -- name: Copy testnet node configurations - copy: - src: "{{playbook_dir}}/testnets/{{TESTNET_NAME}}/node{{nodeid.stdout_lines[0]}}/terrad/config" - dest: "/home/terrad/.terrad" - mode: 0755 - owner: terrad - group: terrad - directory_mode: yes - force: yes +- name: Find gentx file + command: "ls /home/terrad/.terrad/config/gentx" + changed_when: false + register: gentxfile -- name: Copy testnet node data - copy: - src: "{{playbook_dir}}/testnets/{{TESTNET_NAME}}/node{{nodeid.stdout_lines[0]}}/terrad/data" - dest: "/home/terrad/.terrad" - mode: 0755 - owner: terrad - group: terrad - directory_mode: yes - force: yes +- name: Clear local gen-tx list + file: path=files/ state=absent + connection: local + run_once: yes -- name: fix permissions - file: - path: "/home/terrad/.terrad" - owner: terrad - group: terrad - mode: 0755 - recurse: yes +- name: Get gen-tx file + fetch: + dest: files/ + src: "/home/terrad/.terrad/config/gentx/{{gentxfile.stdout_lines[0]}}" + flat: yes - #- name: Update genesis.json to add faucet address - # command: "/usr/bin/terrad add-genesis-account {{FAUCET_ADDRESS}} {{FAUCET_COINS}}" - # become: yes - # become_user: terra +- name: Compress gathered gen-tx files locally + archive: path=files/ exclude_path=files/gen-tx.tgz dest=files/gen-tx.tgz + run_once: yes + connection: local + +- name: Unpack gen-tx archive + unarchive: src=files/gen-tx.tgz dest=/home/terrad/.terrad/config/gentx owner=terrad + +- name: Generate genesis.json + command: "/usr/bin/terrad init --with-txs --name=node{{nodeid.stdout_lines[0]}} --chain-id={{TESTNET_NAME}}" + become: yes + become_user: terrad + args: + creates: /home/terrad/.terrad/config/genesis.json diff --git a/networks/remote/ansible/roles/upgrade-terrad/handlers/main.yml b/networks/remote/ansible/roles/upgrade-gaiad/handlers/main.yml similarity index 100% rename from networks/remote/ansible/roles/upgrade-terrad/handlers/main.yml rename to networks/remote/ansible/roles/upgrade-gaiad/handlers/main.yml diff --git a/networks/remote/ansible/roles/upgrade-terrad/tasks/main.yml b/networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml similarity index 100% rename from networks/remote/ansible/roles/upgrade-terrad/tasks/main.yml rename to networks/remote/ansible/roles/upgrade-gaiad/tasks/main.yml diff --git a/networks/remote/ansible/upgrade-terra.yml b/networks/remote/ansible/upgrade-gaia.yml similarity index 100% rename from networks/remote/ansible/upgrade-terra.yml rename to networks/remote/ansible/upgrade-gaia.yml diff --git a/networks/remote/ansible/upgrade-terrad.yml b/networks/remote/ansible/upgrade-gaiad.yml similarity index 100% rename from networks/remote/ansible/upgrade-terrad.yml rename to networks/remote/ansible/upgrade-gaiad.yml diff --git a/networks/remote/terraform-aws/main.tf b/networks/remote/terraform-aws/main.tf index 719dcb3f8..41e05995e 100644 --- a/networks/remote/terraform-aws/main.tf +++ b/networks/remote/terraform-aws/main.tf @@ -6,7 +6,7 @@ variable "REGIONS" { description = "AWS Regions" type = "list" - default = ["ap-northeast-2", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "sa-east-1"] + default = ["us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "sa-east-1"] } variable "TESTNET_NAME" { @@ -43,7 +43,7 @@ variable "image" { variable "instance_type" { description = "AWS instance type" - default = "t2.micro" + default = "t2.large" } module "nodes-0" { diff --git a/networks/upgrade-terrad.sh b/networks/upgrade-gaiad.sh similarity index 100% rename from networks/upgrade-terrad.sh rename to networks/upgrade-gaiad.sh diff --git a/server/util.go b/server/util.go deleted file mode 100644 index 57a2ed176..000000000 --- a/server/util.go +++ /dev/null @@ -1,241 +0,0 @@ -package server - -import ( - "encoding/json" - "net" - "os" - "os/signal" - "path/filepath" - "syscall" - "time" - - "errors" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/libs/cli" - tmflags "github.com/tendermint/tendermint/libs/cli/flags" - "github.com/tendermint/tendermint/libs/log" - pvm "github.com/tendermint/tendermint/privval" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/config" - "github.com/cosmos/cosmos-sdk/version" -) - -//___________________________________________________________________________________ - -// PersistentPreRunEFn returns a PersistentPreRunE function for cobra -// that initailizes the passed in context with a properly configured -// logger and config object. -func PersistentPreRunEFn(context *server.Context) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - if cmd.Name() == version.VersionCmd.Name() { - return nil - } - config, err := interceptLoadConfig() - if err != nil { - return err - } - err = validateConfig(config) - if err != nil { - return err - } - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) - if err != nil { - return err - } - if viper.GetBool(cli.TraceFlag) { - logger = log.NewTracingLogger(logger) - } - logger = logger.With("module", "main") - context.Config = config - context.Logger = logger - return nil - } -} - -// If a new config is created, change some of the default tendermint settings -func interceptLoadConfig() (conf *cfg.Config, err error) { - tmpConf := cfg.DefaultConfig() - err = viper.Unmarshal(tmpConf) - - if err != nil { - return tmpConf, err - } - - rootDir := tmpConf.RootDir - configFilePath := filepath.Join(rootDir, "config/config.toml") - // Intercept only if the file doesn't already exist - - if _, err := os.Stat(configFilePath); os.IsNotExist(err) { - // the following parse config is needed to create directories - conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. - conf.ProfListenAddress = "localhost:6060" - conf.P2P.RecvRate = 5120000 - conf.P2P.SendRate = 5120000 - conf.TxIndex.IndexAllTags = true - conf.Consensus.TimeoutCommit = 5 * time.Second - cfg.WriteConfigFile(configFilePath, conf) - // Fall through, just so that its parsed into memory. - } - - if conf == nil { - conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. - } - - // create a default terra config file if it does not exist - terraConfigFilePath := filepath.Join(rootDir, "config/terrad.toml") - if _, err := os.Stat(terraConfigFilePath); os.IsNotExist(err) { - terraConf, _ := config.ParseConfig() - config.WriteConfigFile(terraConfigFilePath, terraConf) - } - - viper.SetConfigName("terrad") - err = viper.MergeInConfig() - - return -} - -// validate the config with the sdk's requirements. -func validateConfig(conf *cfg.Config) error { - if !conf.Consensus.CreateEmptyBlocks { - return errors.New("config option CreateEmptyBlocks = false is currently unsupported") - } - return nil -} - -// add server commands -func AddCommands( - ctx *server.Context, cdc *codec.Codec, - rootCmd *cobra.Command, - appCreator server.AppCreator, appExport server.AppExporter) { - - rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") - - tendermintCmd := &cobra.Command{ - Use: "tendermint", - Short: "Tendermint subcommands", - } - - tendermintCmd.AddCommand( - server.ShowNodeIDCmd(ctx), - server.ShowValidatorCmd(ctx), - server.ShowAddressCmd(ctx), - server.VersionCmd(ctx), - ) - - rootCmd.AddCommand( - server.StartCmd(ctx, appCreator), - server.UnsafeResetAllCmd(ctx), - client.LineBreak, - tendermintCmd, - server.ExportCmd(ctx, cdc, appExport), - client.LineBreak, - version.VersionCmd, - ) -} - -//___________________________________________________________________________________ - -// InsertKeyJSON inserts a new JSON field/key with a given value to an existing -// JSON message. An error is returned if any serialization operation fails. -// -// NOTE: The ordering of the keys returned as the resulting JSON message is -// non-deterministic, so the client should not rely on key ordering. -func InsertKeyJSON(cdc *codec.Codec, baseJSON []byte, key string, value json.RawMessage) ([]byte, error) { - var jsonMap map[string]json.RawMessage - - if err := cdc.UnmarshalJSON(baseJSON, &jsonMap); err != nil { - return nil, err - } - - jsonMap[key] = value - bz, err := codec.MarshalJSONIndent(cdc, jsonMap) - - return json.RawMessage(bz), err -} - -// https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go -// TODO there must be a better way to get external IP -func ExternalIP() (string, error) { - ifaces, err := net.Interfaces() - if err != nil { - return "", err - } - for _, iface := range ifaces { - if skipInterface(iface) { - continue - } - addrs, err := iface.Addrs() - if err != nil { - return "", err - } - for _, addr := range addrs { - ip := addrToIP(addr) - if ip == nil || ip.IsLoopback() { - continue - } - ip = ip.To4() - if ip == nil { - continue // not an ipv4 address - } - return ip.String(), nil - } - } - return "", errors.New("are you connected to the network?") -} - -// TrapSignal traps SIGINT and SIGTERM and terminates the server correctly. -func TrapSignal(cleanupFunc func()) { - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - go func() { - sig := <-sigs - switch sig { - case syscall.SIGTERM: - defer cleanupFunc() - os.Exit(128 + int(syscall.SIGTERM)) - case syscall.SIGINT: - defer cleanupFunc() - os.Exit(128 + int(syscall.SIGINT)) - } - }() -} - -// UpgradeOldPrivValFile converts old priv_validator.json file (prior to Tendermint 0.28) -// to the new priv_validator_key.json and priv_validator_state.json files. -func UpgradeOldPrivValFile(config *cfg.Config) { - if _, err := os.Stat(config.OldPrivValidatorFile()); !os.IsNotExist(err) { - if oldFilePV, err := pvm.LoadOldFilePV(config.OldPrivValidatorFile()); err == nil { - oldFilePV.Upgrade(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - } - } -} - -func skipInterface(iface net.Interface) bool { - if iface.Flags&net.FlagUp == 0 { - return true // interface down - } - if iface.Flags&net.FlagLoopback != 0 { - return true // loopback interface - } - return false -} - -func addrToIP(addr net.Addr) net.IP { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - return ip -} diff --git a/sims.mk b/sims.mk new file mode 100644 index 000000000..149956155 --- /dev/null +++ b/sims.mk @@ -0,0 +1,62 @@ +#!/usr/bin/make -f + +######################################## +### Simulations + +SIMAPP = github.com/terra-project/core/app + +sim-terra-nondeterminism: + @echo "Running nondeterminism test..." + @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ + -NumBlocks=100 -BlockSize=200 -Commit=true -v -timeout 24h + +sim-terra-custom-genesis-fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.terrad/config/genesis.json will be used." + @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.gaiad/config/genesis.json \ + -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h + +sim-terra-fast: + @echo "Running quick Terra simulation. This may take several minutes..." + @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h + +sim-terra-import-export: runsim + @echo "Running Terra import/export simulation. This may take several minutes..." + $(GOPATH)/bin/runsim $(SIMAPP) 25 5 TestAppImportExport + +sim-terra-simulation-after-import: runsim + @echo "Running Terra simulation-after-import. This may take several minutes..." + $(GOPATH)/bin/runsim $(SIMAPP) 25 5 TestAppSimulationAfterImport + +sim-terra-custom-genesis-multi-seed: runsim + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.terrad/config/genesis.json will be used." + $(GOPATH)/bin/runsim -g ${HOME}/.terrad/config/genesis.json $(SIMAPP) 400 5 TestFullAppSimulation + +sim-terra-multi-seed: runsim + @echo "Running multi-seed Terra simulation. This may take awhile!" + $(GOPATH)/bin/runsim $(SIMAPP) 400 5 TestFullAppSimulation + +sim-benchmark-invariants: + @echo "Running simulation invariant benchmarks..." + @go test -mod=readonly github.com/terra-project/core/app -benchmem -bench=BenchmarkInvariants -run=^$ \ + -Enabled=true -NumBlocks=1000 -BlockSize=200 \ + -Commit=true -Seed=57 -v -timeout 24h + +SIM_NUM_BLOCKS ?= 500 +SIM_BLOCK_SIZE ?= 200 +SIM_COMMIT ?= true +sim-terra-benchmark: + @echo "Running Terra benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" + @go test -mod=readonly -benchmem -run=^$$ github.com/terra-project/core/app -bench ^BenchmarkFullAppSimulation$$ \ + -Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h + +sim-terra-profile: + @echo "Running Terra benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" + @go test -mod=readonly -benchmem -run=^$$ github.com/terra-project/core/app -bench ^BenchmarkFullAppSimulation$$ \ + -Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out + + +.PHONY: runsim sim-terra-nondeterminism sim-terra-custom-genesis-fast sim-terra-fast sim-terra-import-export \ + sim-terra-simulation-after-import sim-terra-custom-genesis-multi-seed sim-terra-multi-seed \ + sim-benchmark-invariants sim-terra-benchmark sim-terra-profile diff --git a/testutil/cmd.go b/testutil/cmd.go deleted file mode 100644 index 15b7808ed..000000000 --- a/testutil/cmd.go +++ /dev/null @@ -1,71 +0,0 @@ -package testutil - -import ( - "bytes" - - "github.com/spf13/cobra" - - "github.com/tendermint/tendermint/libs/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/terra-project/core/app" - "github.com/terra-project/core/types/util" -) - -// PrepareTest prepares codec, rootCmd, txCmd, queryCmd instances -func PrepareCmdTest() (cdc *codec.Codec, rootCmd *cobra.Command, txCmd *cobra.Command, queryCmd *cobra.Command) { - cdc = app.MakeCodec() - - config := sdk.GetConfig() - - if config.GetBech32AccountAddrPrefix() != util.Bech32PrefixAccAddr { - config.SetCoinType(330) - config.SetFullFundraiserPath("44'/330'/0'/0/0") - config.SetBech32PrefixForAccount(util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(util.Bech32PrefixValAddr, util.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub) - config.Seal() - } - - rootCmd = &cobra.Command{ - Use: "terracli", - Short: "Command line interface for interacting with terrad", - } - - txCmd = &cobra.Command{ - Use: "tx", - Short: "Transactions subcommands", - } - - queryCmd = &cobra.Command{ - Use: "query", - Aliases: []string{"q"}, - Short: "Querying subcommands", - } - - rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node") - rootCmd.AddCommand(txCmd) - rootCmd.AddCommand(queryCmd) - - return -} - -// ExecuteCommand executes command -func ExecuteCommand(rootCmd *cobra.Command, args ...string) (output string, err error) { - output, err = executeCommandC(rootCmd, args...) - return output, err -} - -func executeCommandC(rootCmd *cobra.Command, args ...string) (output string, err error) { - buf := new(bytes.Buffer) - rootCmd.SetOutput(buf) - rootCmd.SetArgs(args) - - executor := cli.PrepareMainCmd(rootCmd, "TE", app.DefaultCLIHome) - err = executor.Execute() - - return buf.String(), err -} diff --git a/testutil/func.go b/testutil/func.go deleted file mode 100644 index 80b3632c2..000000000 --- a/testutil/func.go +++ /dev/null @@ -1,10 +0,0 @@ -package testutil - -import ( - "fmt" -) - -// FS returns function signature to compare -func FS(f interface{}) string { - return fmt.Sprintf("%v", f) -} diff --git a/tools-stamp b/tools-stamp new file mode 100644 index 000000000..e69de29bb diff --git a/types/alias.go b/types/alias.go new file mode 100644 index 000000000..6cbf87f57 --- /dev/null +++ b/types/alias.go @@ -0,0 +1,44 @@ +// Package types nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/types/assets/ +// ALIASGEN: github.com/terra-project/core/types/util/ +package types + +import ( + "github.com/terra-project/core/types/assets" + "github.com/terra-project/core/types/util" +) + +const ( + MicroLunaDenom = assets.MicroLunaDenom + MicroUSDDenom = assets.MicroUSDDenom + MicroKRWDenom = assets.MicroKRWDenom + MicroSDRDenom = assets.MicroSDRDenom + MicroCNYDenom = assets.MicroCNYDenom + MicroJPYDenom = assets.MicroJPYDenom + MicroEURDenom = assets.MicroEURDenom + MicroGBPDenom = assets.MicroGBPDenom + MicroUnit = assets.MicroUnit + BlocksPerMinute = util.BlocksPerMinute + BlocksPerHour = util.BlocksPerHour + BlocksPerDay = util.BlocksPerDay + BlocksPerWeek = util.BlocksPerWeek + BlocksPerMonth = util.BlocksPerMonth + BlocksPerYear = util.BlocksPerYear + BlocksPerEpoch = util.BlocksPerEpoch + CoinType = util.CoinType + FullFundraiserPath = util.FullFundraiserPath + Bech32PrefixAccAddr = util.Bech32PrefixAccAddr + Bech32PrefixAccPub = util.Bech32PrefixAccPub + Bech32PrefixValAddr = util.Bech32PrefixValAddr + Bech32PrefixValPub = util.Bech32PrefixValPub + Bech32PrefixConsAddr = util.Bech32PrefixConsAddr + Bech32PrefixConsPub = util.Bech32PrefixConsPub +) + +var ( + // functions aliases + GetEpoch = util.GetEpoch + IsPeriodLastBlock = util.IsPeriodLastBlock +) diff --git a/types/codec.go b/types/codec.go deleted file mode 100644 index 5b7139cac..000000000 --- a/types/codec.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -import "github.com/cosmos/cosmos-sdk/codec" - -// RegisterCodec register type codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(&Schedule{}, "core/Schedule", nil) - cdc.RegisterConcrete(&VestingSchedule{}, "core/VestingSchedule", nil) - cdc.RegisterConcrete(&BaseGradedVestingAccount{}, "core/GradedVestingAccount", nil) - cdc.RegisterConcrete(&BaseLazyGradedVestingAccount{}, "core/LazyGradedVestingAccount", nil) -} diff --git a/types/graded_account.go b/types/graded_account.go deleted file mode 100644 index 70fa4eb70..000000000 --- a/types/graded_account.go +++ /dev/null @@ -1,304 +0,0 @@ -package types - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" -) - -//----------------------------------------------------------------------------- -// Schedule - -// Schedule no-lint -type Schedule struct { - Cliff int64 `json:"cliff"` - Ratio sdk.Dec `json:"ratio"` -} - -func NewSchedule(cliff int64, ratio sdk.Dec) Schedule { - return Schedule{ - Cliff: cliff, - Ratio: ratio, - } -} - -// GetRatio returns cliff -func (s Schedule) GetCliff() int64 { - return s.Cliff -} - -// GetRatio returns ratio -func (s Schedule) GetRatio() sdk.Dec { - return s.Ratio -} - -// String implements the fmt.Stringer interface -func (s Schedule) String() string { - return fmt.Sprintf(`Schedule: - Cliff: %v, - Ratio: %v`, - s.Cliff, s.Ratio) -} - -// IsValid checks that the schedule is valid. -func (s Schedule) IsValid() bool { - - cliff := s.GetCliff() - ratio := s.GetRatio() - - return cliff >= 0 && ratio.GT(sdk.ZeroDec()) -} - -//----------------------------------------------------------------------------- -// Vesting Schedule - -// VestingSchedule maps the ratio of tokens that becomes vested by blocktime (in nanoseconds) from genesis. -// The sum of values in the Schedule should sum to 1.0. -// CONTRACT: assumes that entries are -type VestingSchedule struct { - Denom string `json:"denom"` - Schedules []Schedule `json:"schedules"` // maps blocktime to percentage vested. Should sum to 1. -} - -// NewVestingSchedule creates a new vesting schedule instance. -func NewVestingSchedule(denom string, schedules []Schedule) VestingSchedule { - return VestingSchedule{ - Denom: denom, - Schedules: schedules, - } -} - -// GetVestedRatio returns the ratio of tokens that have vested by blockTime. -func (vs VestingSchedule) GetVestedRatio(blockTime int64) sdk.Dec { - sumRatio := sdk.ZeroDec() - for _, schedule := range vs.Schedules { - cliff := schedule.GetCliff() - ratio := schedule.GetRatio() - - if blockTime >= cliff { - sumRatio = sumRatio.Add(ratio) - } - } - return sumRatio -} - -// GetDenom returns the denom of vesting schedule -func (vs VestingSchedule) GetDenom() string { - return vs.Denom -} - -// IsValid checks that the vestingschedule is valid. -func (vs VestingSchedule) IsValid() bool { - sumRatio := sdk.ZeroDec() - for _, schedule := range vs.Schedules { - - if !schedule.IsValid() { - return false - } - - sumRatio = sumRatio.Add(schedule.GetRatio()) - } - - return sumRatio.Equal(sdk.OneDec()) -} - -// String implements the fmt.Stringer interface -func (vs VestingSchedule) String() string { - return fmt.Sprintf(`VestingSchedule: - Denom: %v, - Schedules: %v`, - vs.Denom, vs.Schedules) -} - -//----------------------------------------------------------------------------- -// Graded Vesting Account - -// GradedVestingAccount defines an account type that vests coins via a graded vesting schedule. -type GradedVestingAccount interface { - auth.VestingAccount - - GetVestingSchedules() []VestingSchedule - GetVestingSchedule(denom string) (VestingSchedule, bool) -} - -// BaseGradedVestingAccount implements the GradedVestingAccount interface. It vests all -// coins according to a predefined schedule. -var _ GradedVestingAccount = (*BaseGradedVestingAccount)(nil) - -// BaseGradedVestingAccount implements the VestingAccount interface. It vests tokens according to -// a predefined set of vesting cliffs. -type BaseGradedVestingAccount struct { - *auth.BaseVestingAccount - - VestingSchedules []VestingSchedule `json:"vesting_schedules"` -} - -// NewBaseGradedVestingAccount returns a BaseGradedVestingAccount -func NewBaseGradedVestingAccount(baseAcc *auth.BaseAccount, vestingSchedules []VestingSchedule) *BaseGradedVestingAccount { - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: baseAcc, - OriginalVesting: baseAcc.Coins, - EndTime: 0, - } - - return &BaseGradedVestingAccount{baseVestingAcc, vestingSchedules} -} - -// GetSchedules returns the VestingSchedules of the graded vesting account -func (gva BaseGradedVestingAccount) GetVestingSchedules() []VestingSchedule { - return gva.VestingSchedules -} - -// GetVestingSchedule returns the VestingSchedule of the given denom -func (gva BaseGradedVestingAccount) GetVestingSchedule(denom string) (VestingSchedule, bool) { - for _, vs := range gva.VestingSchedules { - if vs.Denom == denom { - return vs, true - } - } - - return VestingSchedule{}, false -} - -// GetVestedCoins returns the total amount of vested coins for a graded vesting -// account. All coins are only vested once the schedule has elapsed. -func (gva BaseGradedVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins { - var vestedCoins sdk.Coins - for _, ovc := range gva.OriginalVesting { - if vestingSchedule, exists := gva.GetVestingSchedule(ovc.Denom); exists { - vestedRatio := vestingSchedule.GetVestedRatio(blockTime.Unix()) - vestedAmt := ovc.Amount.ToDec().Mul(vestedRatio).RoundInt() - if vestedAmt.Equal(sdk.ZeroInt()) { - continue - } - vestedCoins = append(vestedCoins, sdk.NewCoin(ovc.Denom, vestedAmt)) - } else { - vestedCoins = append(vestedCoins, sdk.NewCoin(ovc.Denom, ovc.Amount)) - } - } - - return vestedCoins -} - -// GetVestingCoins returns the total number of vesting coins for a graded -// vesting account. -func (gva BaseGradedVestingAccount) GetVestingCoins(blockTime time.Time) sdk.Coins { - return gva.OriginalVesting.Sub(gva.GetVestedCoins(blockTime)) -} - -// SpendableCoins returns the total number of spendable coins for a graded -// vesting account. -func (gva BaseGradedVestingAccount) SpendableCoins(blockTime time.Time) sdk.Coins { - return gva.spendableCoins(gva.GetVestingCoins(blockTime)) -} - -// TrackDelegation tracks a desired delegation amount by setting the appropriate -// values for the amount of delegated vesting, delegated free, and reducing the -// overall amount of base coins. -func (gva *BaseGradedVestingAccount) TrackDelegation(blockTime time.Time, amount sdk.Coins) { - gva.trackDelegation(gva.GetVestingCoins(blockTime), amount) -} - -// GetStartTime returns zero since a graded vesting account has no start time. -func (gva *BaseGradedVestingAccount) GetStartTime() int64 { - return 0 -} - -// GetEndTime returns the time when vesting ends for a graded vesting account. -func (gva *BaseGradedVestingAccount) GetEndTime() int64 { - return 0 -} - -func (gva BaseGradedVestingAccount) String() string { - var pubkey string - - if gva.PubKey != nil { - pubkey = sdk.MustBech32ifyAccPub(gva.PubKey) - } - - return fmt.Sprintf(`Graded Vesting Account: - Address: %s - Pubkey: %s - Coins: %s - AccountNumber: %d - Sequence: %d - OriginalVesting: %s - DelegatedFree: %s - DelegatedVesting: %s - VestingSchedules: %v `, - gva.Address, pubkey, gva.Coins, gva.AccountNumber, gva.Sequence, - gva.OriginalVesting, gva.DelegatedFree, gva.DelegatedVesting, - gva.VestingSchedules, - ) -} - -// spendableCoins returns all the spendable coins for a vesting account given a -// set of vesting coins. -// -// CONTRACT: The account's coins, delegated vesting coins, vestingCoins must be -// sorted. -func (gva BaseGradedVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins { - var spendableCoins sdk.Coins - bc := gva.GetCoins() - - for _, coin := range bc { - // zip/lineup all coins by their denomination to provide O(n) time - baseAmt := coin.Amount - vestingAmt := vestingCoins.AmountOf(coin.Denom) - delVestingAmt := gva.DelegatedVesting.AmountOf(coin.Denom) - - // compute min((BC + DV) - V, BC) per the specification - min := sdk.MinInt(baseAmt.Add(delVestingAmt).Sub(vestingAmt), baseAmt) - spendableCoin := sdk.NewCoin(coin.Denom, min) - - if !spendableCoin.IsZero() { - spendableCoins = spendableCoins.Add(sdk.Coins{spendableCoin}) - } - } - - return spendableCoins -} - -// trackDelegation tracks a delegation amount for any given vesting account type -// given the amount of coins currently vesting. It returns the resulting base -// coins. -// -// CONTRACT: The account's coins, delegation coins, vesting coins, and delegated -// vesting coins must be sorted. -func (gva *BaseGradedVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) { - bc := gva.GetCoins() - - for _, coin := range amount { - // zip/lineup all coins by their denomination to provide O(n) time - - baseAmt := bc.AmountOf(coin.Denom) - vestingAmt := vestingCoins.AmountOf(coin.Denom) - delVestingAmt := gva.DelegatedVesting.AmountOf(coin.Denom) - - // Panic if the delegation amount is zero or if the base coins does not - // exceed the desired delegation amount. - if coin.Amount.IsZero() || baseAmt.LT(coin.Amount) { - panic("delegation attempt with zero coins or insufficient funds") - } - - // compute x and y per the specification, where: - // X := min(max(V - DV, 0), D) - // Y := D - X - x := sdk.MinInt(sdk.MaxInt(vestingAmt.Sub(delVestingAmt), sdk.ZeroInt()), coin.Amount) - y := coin.Amount.Sub(x) - - if !x.IsZero() { - xCoin := sdk.NewCoin(coin.Denom, x) - gva.DelegatedVesting = gva.DelegatedVesting.Add(sdk.Coins{xCoin}) - } - - if !y.IsZero() { - yCoin := sdk.NewCoin(coin.Denom, y) - gva.DelegatedFree = gva.DelegatedFree.Add(sdk.Coins{yCoin}) - } - - gva.Coins = gva.Coins.Sub(sdk.Coins{coin}) - } -} diff --git a/types/graded_account_test.go b/types/graded_account_test.go deleted file mode 100644 index 8800f146e..000000000 --- a/types/graded_account_test.go +++ /dev/null @@ -1,399 +0,0 @@ -package types - -import ( - "testing" - "time" - - "github.com/terra-project/core/types/assets" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/stretchr/testify/require" -) - -func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { - key := secp256k1.GenPrivKey() - pub := key.PubKey() - addr := sdk.AccAddress(pub.Address()) - return key, pub, addr -} - -var ( - angelSchedule VestingSchedule - seedSchedule VestingSchedule - privateSchedule VestingSchedule - privateBonusSchedule VestingSchedule - employeeSchedule VestingSchedule - timeGenesisString = "2019-04-23 23:00:00 -0800 PST" - monthlyTimes []int64 - timeGenesis time.Time -) - -// initialize the times! -func init() { - var err error - timeLayoutString := "2006-01-02 15:04:05 -0700 MST" - timeGenesis, err = time.Parse(timeLayoutString, timeGenesisString) - if err != nil { - panic(err) - } - - timeGenesis = timeGenesis.UTC() - - monthlyTimes = []int64{} - for i := 0; i < 4; i++ { - for j := 0; j < 12; j++ { - monthlyTimes = append(monthlyTimes, timeGenesis.AddDate(i, j, 0).Unix()) - } - } - - angelSchedule = NewVestingSchedule(assets.MicroLunaDenom, []Schedule{ - NewSchedule(monthlyTimes[1], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[12], sdk.NewDecWithPrec(70, 2)), - }) - - seedSchedule = NewVestingSchedule(assets.MicroLunaDenom, []Schedule{ - NewSchedule(monthlyTimes[1], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), - NewSchedule(monthlyTimes[10], sdk.NewDecWithPrec(70, 2)), - }) - - privateSchedule = NewVestingSchedule(assets.MicroLunaDenom, []Schedule{ - NewSchedule(monthlyTimes[3], sdk.NewDecWithPrec(16, 2)), - NewSchedule(monthlyTimes[4], sdk.NewDecWithPrec(17, 2)), - NewSchedule(monthlyTimes[5], sdk.NewDecWithPrec(16, 2)), - NewSchedule(monthlyTimes[6], sdk.NewDecWithPrec(17, 2)), - NewSchedule(monthlyTimes[7], sdk.NewDecWithPrec(17, 2)), - NewSchedule(monthlyTimes[8], sdk.NewDecWithPrec(17, 2)), - }) - - privateBonusSchedule = NewVestingSchedule(assets.MicroLunaDenom, []Schedule{ - NewSchedule(monthlyTimes[6], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[7], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[8], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[9], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[10], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[11], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[12], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[13], sdk.NewDecWithPrec(8, 2)), - NewSchedule(monthlyTimes[14], sdk.NewDecWithPrec(9, 2)), - NewSchedule(monthlyTimes[15], sdk.NewDecWithPrec(9, 2)), - NewSchedule(monthlyTimes[16], sdk.NewDecWithPrec(9, 2)), - NewSchedule(monthlyTimes[17], sdk.NewDecWithPrec(9, 2)), - }) - - employeeSchedule = NewVestingSchedule(assets.MicroLunaDenom, []Schedule{ - NewSchedule(monthlyTimes[0], sdk.NewDecWithPrec(5, 2)), - NewSchedule(monthlyTimes[12], sdk.NewDecWithPrec(29, 2)), - NewSchedule(monthlyTimes[24], sdk.NewDecWithPrec(33, 2)), - NewSchedule(monthlyTimes[36], sdk.NewDecWithPrec(33, 2)), - }) - -} - -func scaleCoins(scale float64, denom string, input sdk.Coins) sdk.Coins { - output := sdk.Coins{} - for _, coin := range input { - if coin.Denom != denom { - continue - } - - decScale := sdk.NewDecWithPrec(int64(scale*100), 2) - output = append(output, sdk.NewCoin(coin.Denom, decScale.MulInt(coin.Amount).RoundInt())) - } - return output -} - -func TestGetVestedCoinsGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - vestedCoins := gva.GetVestedCoins(genesis) - require.Nil(t, vestedCoins) - - // require coins be vested at the expected cliff - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(0, 1, 0)) - require.Equal(t, scaleCoins(0.1, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(0, 2, 0)) - require.Equal(t, scaleCoins(0.2, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(0, 3, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(0, 4, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(0, 5, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = gva.GetVestedCoins(timeGenesis.AddDate(1, 0, 0)) - require.Equal(t, scaleCoins(1.0, assets.MicroLunaDenom, origCoins), vestedCoins) -} - -func TestGetVestingCoinsGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - vestingCoins := gva.GetVestingCoins(genesis) - require.Equal(t, vestingCoins, origCoins) - - // require coins be vested at the expected cliff - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(0, 1, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.1, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(0, 2, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.2, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(0, 3, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(0, 4, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(0, 5, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = gva.GetVestingCoins(timeGenesis.AddDate(1, 0, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(1.0, assets.MicroLunaDenom, origCoins)), vestingCoins) -} - -func TestSpendableCoinsGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - sdrCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - - spendableCoins := gva.SpendableCoins(genesis) - require.Equal(t, sdrCoins, spendableCoins) - - // require that all coins are spendable after the maturation of the vesting - // schedule - spendableCoins = gva.SpendableCoins(timeGenesis.AddDate(1, 0, 0)) - require.Equal(t, origCoins, spendableCoins) - - // require that all luna coins are still vesting after some time - spendableCoins = gva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.Equal(t, spendableCoins, sdrCoins) - - // receive some coins - regvamt := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 50)} - gva.SetCoins(gva.GetCoins().Add(regvamt)) - - // require that only received coins and sdrCoins are spendable since the account is still - // vesting - spendableCoins = gva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.Equal(t, regvamt.Add(sdrCoins), spendableCoins) - - // spend all spendable coins - gva.SetCoins(gva.GetCoins().Sub(spendableCoins)) - - // require that no more coins are spendable - spendableCoins = gva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.Nil(t, spendableCoins) -} - -func TestTrackDelegationGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - - bacc.SetCoins(origCoins) - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(genesis, origCoins) - require.Equal(t, origCoins, gva.DelegatedVesting) - require.Nil(t, gva.DelegatedFree) - require.Nil(t, gva.GetCoins()) - - // require the ability to delegate all vested coins - bacc.SetCoins(origCoins) - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(timeGenesis.AddDate(1, 0, 0), origCoins) - require.Nil(t, gva.DelegatedVesting) - require.Equal(t, origCoins, gva.DelegatedFree) - require.Nil(t, gva.GetCoins()) - - // require the ability to delegate all coins half way through the vesting - // schedule - bacc.SetCoins(origCoins) - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, gva.DelegatedVesting) - require.Nil(t, gva.DelegatedFree) - - gva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, gva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, gva.DelegatedFree) - require.Nil(t, gva.GetCoins()) - - // require no modifications when delegation amount is zero or not enough funds - bacc.SetCoins(origCoins) - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - - require.Panics(t, func() { - gva.TrackDelegation(genesis.AddDate(1, 0, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 1000000)}) - }) - require.Nil(t, gva.DelegatedVesting) - require.Nil(t, gva.DelegatedFree) - require.Equal(t, origCoins, gva.GetCoins()) -} - -func TestTrackUndelegationGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(genesis, origCoins) - gva.TrackUndelegation(origCoins) - require.Nil(t, gva.DelegatedFree) - require.Nil(t, gva.DelegatedVesting) - require.Equal(t, origCoins, gva.GetCoins()) - - // require the ability to undelegate all vested coins - bacc.SetCoins(origCoins) - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(genesis.AddDate(1, 0, 0), origCoins) - gva.TrackUndelegation(origCoins) - require.Nil(t, gva.DelegatedFree) - require.Nil(t, gva.DelegatedVesting) - require.Equal(t, origCoins, gva.GetCoins()) - - // require no modifications when the undelegation amount is zero - bacc.SetCoins(origCoins) - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - require.Panics(t, func() { - gva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 0)}) - }) - require.Nil(t, gva.DelegatedFree) - require.Nil(t, gva.DelegatedVesting) - require.Equal(t, origCoins, gva.GetCoins()) - - // vest 50% and delegate to two validators - gva = NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - gva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - gva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - - // undelegate from one validator that got slashed 50% - gva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 500)}, gva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, gva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, gva.GetCoins()) - - // undelegate from the other validator that did not get slashed - gva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - require.Nil(t, gva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}, gva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, gva.GetCoins()) -} - -func TestStringGradVestingAcc(t *testing.T) { - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - gva := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - require.NotNil(t, gva.String()) - - vestingSchedule, found := gva.GetVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.NotNil(t, vestingSchedule.String()) -} - -func TestIsValidGradVestingAcc(t *testing.T) { - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - angelAccount := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - angelSchedule, - }) - - vestingSchedule, found := angelAccount.GetVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, vestingSchedule.IsValid()) - - seedAccount := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - seedSchedule, - }) - - vestingSchedule, found = seedAccount.GetVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, vestingSchedule.IsValid()) - - privateAccount := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - privateSchedule, - }) - - vestingSchedule, found = privateAccount.GetVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, vestingSchedule.IsValid()) - - employeeAccount := NewBaseGradedVestingAccount(&bacc, []VestingSchedule{ - employeeSchedule, - }) - - vestingSchedule, found = employeeAccount.GetVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, vestingSchedule.IsValid()) -} diff --git a/types/lazy_graded_account_test.go b/types/lazy_graded_account_test.go deleted file mode 100644 index 418644be9..000000000 --- a/types/lazy_graded_account_test.go +++ /dev/null @@ -1,361 +0,0 @@ -package types - -import ( - "testing" - "time" - - "github.com/terra-project/core/types/assets" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/stretchr/testify/require" -) - -var ( - angelLazySchedule LazyVestingSchedule - seedLazySchedule LazyVestingSchedule - privateLazySchedule LazyVestingSchedule - privateBonusLazySchedule LazyVestingSchedule -) - -// initialize the times! -func init() { - var err error - timeLayoutString := "2006-01-02 15:04:05 -0700 MST" - timeGenesis, err = time.Parse(timeLayoutString, timeGenesisString) - if err != nil { - panic(err) - } - - timeGenesis = timeGenesis.UTC() - - monthlyTimes = []int64{} - for i := 0; i < 4; i++ { - for j := 0; j < 12; j++ { - monthlyTimes = append(monthlyTimes, timeGenesis.AddDate(i, j, 0).Unix()) - } - } - - angelLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ - NewLazySchedule(monthlyTimes[1], monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[2], monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[3], monthlyTimes[4], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[12], monthlyTimes[13], sdk.NewDecWithPrec(70, 2)), - }) - - seedLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ - NewLazySchedule(monthlyTimes[1], monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[2], monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[3], monthlyTimes[4], sdk.NewDecWithPrec(10, 2)), - NewLazySchedule(monthlyTimes[10], monthlyTimes[11], sdk.NewDecWithPrec(70, 2)), - }) - - privateLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ - NewLazySchedule(monthlyTimes[3], monthlyTimes[9], sdk.NewDec(1)), - }) - - privateBonusLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ - NewLazySchedule(monthlyTimes[6], monthlyTimes[18], sdk.NewDec(1)), - }) - -} - -func TestGetVestedCoinsLazyGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - vestedCoins := lgva.GetVestedCoins(genesis) - require.Nil(t, vestedCoins) - - // require coins be vested at the expected cliff - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 1, 0)) - require.True(t, vestedCoins.Empty()) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 2, 0)) - require.Equal(t, scaleCoins(0.1, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 2, 15)) - require.Equal(t, scaleCoins(0.15, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 3, 0)) - require.Equal(t, scaleCoins(0.2, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 4, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 5, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(1, 0, 0)) - require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) - - vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(1, 1, 0)) - require.Equal(t, scaleCoins(1.0, assets.MicroLunaDenom, origCoins), vestedCoins) -} - -func TestGetVestingCoinsLazyGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - vestingCoins := lgva.GetVestingCoins(genesis) - require.Equal(t, vestingCoins, origCoins) - - // require coins be vested at the expected cliff - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 1, 0)) - require.Equal(t, origCoins, vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 2, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.1, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 2, 15)) - require.Equal(t, origCoins.Sub(scaleCoins(0.15, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 3, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.2, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 4, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 5, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(1, 0, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) - - vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(1, 1, 0)) - require.Equal(t, origCoins.Sub(scaleCoins(1.0, assets.MicroLunaDenom, origCoins)), vestingCoins) -} - -func TestSpendableCoinsLazyGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - sdrCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require no coins are vested until schedule maturation - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - - spendableCoins := lgva.SpendableCoins(genesis) - require.Equal(t, sdrCoins, spendableCoins) - - // require that all coins are spendable after the maturation of the vesting - // schedule - spendableCoins = lgva.SpendableCoins(timeGenesis.AddDate(1, 1, 0)) - require.Equal(t, origCoins, spendableCoins) - - // require that all luna coins are still vesting after some time - spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.Equal(t, spendableCoins, sdrCoins) - - // receive some coins - relgvamt := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 50)} - lgva.SetCoins(lgva.GetCoins().Add(relgvamt)) - - // require that only received coins and sdrCoins are spendable since the account is still - // vesting - spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.Equal(t, relgvamt.Add(sdrCoins), spendableCoins) - - // spend all spendable coins - lgva.SetCoins(lgva.GetCoins().Sub(spendableCoins)) - - // require that no more coins are spendable - spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) - require.True(t, spendableCoins.Empty()) -} - -func TestTrackDelegationLazyGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - - bacc.SetCoins(origCoins) - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis, origCoins) - require.Equal(t, origCoins, lgva.DelegatedVesting) - require.Nil(t, lgva.DelegatedFree) - require.Nil(t, lgva.GetCoins()) - - // require the ability to delegate all vested coins - bacc.SetCoins(origCoins) - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis.AddDate(1, 1, 0), origCoins) - require.Nil(t, lgva.DelegatedVesting) - require.Equal(t, origCoins, lgva.DelegatedFree) - require.Nil(t, lgva.GetCoins()) - - // require the ability to delegate all coins half way through the vesting - // schedule - bacc.SetCoins(origCoins) - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, lgva.DelegatedVesting) - require.Nil(t, lgva.DelegatedFree) - - lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, lgva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, lgva.DelegatedFree) - require.Nil(t, lgva.GetCoins()) - - // require no modifications when delegation amount is zero or not enough funds - bacc.SetCoins(origCoins) - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - - require.Panics(t, func() { - lgva.TrackDelegation(genesis.AddDate(1, 0, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 1000000)}) - }) - require.Nil(t, lgva.DelegatedVesting) - require.Nil(t, lgva.DelegatedFree) - require.Equal(t, origCoins, lgva.GetCoins()) -} - -func TestTrackUndelegationLazyGradVestingAcc(t *testing.T) { - genesis := timeGenesis - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis, origCoins) - lgva.TrackUndelegation(origCoins) - require.Nil(t, lgva.DelegatedFree) - require.Nil(t, lgva.DelegatedVesting) - require.Equal(t, origCoins, lgva.GetCoins()) - - // require the ability to undelegate all vested coins - bacc.SetCoins(origCoins) - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis.AddDate(1, 1, 0), origCoins) - lgva.TrackUndelegation(origCoins) - require.Nil(t, lgva.DelegatedFree) - require.Nil(t, lgva.DelegatedVesting) - require.Equal(t, origCoins, lgva.GetCoins()) - - // require no modifications when the undelegation amount is zero - bacc.SetCoins(origCoins) - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - require.Panics(t, func() { - lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 0)}) - }) - require.Nil(t, lgva.DelegatedFree) - require.Nil(t, lgva.DelegatedVesting) - require.Equal(t, origCoins, lgva.GetCoins()) - - // vest 50% and delegate to two validators - lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - - // undelegate from one validator that got slashed 50% - lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 500)}, lgva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, lgva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, lgva.GetCoins()) - - // undelegate from the other validator that did not get slashed - lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) - require.Nil(t, lgva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}, lgva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, lgva.GetCoins()) -} - -func TestStringLazyGradVestingAcc(t *testing.T) { - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - require.NotNil(t, lgva.String()) - - lazyVestingSchedule, found := lgva.GetLazyVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.NotNil(t, lazyVestingSchedule.String()) -} - -func TestIsValidLazyGradVestingAcc(t *testing.T) { - - _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} - bacc := auth.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - - // require the ability to undelegate all vesting coins - angelAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - angelLazySchedule, - }) - - lazyVestingSchedule, found := angelAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, lazyVestingSchedule.IsValid()) - - seedAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - seedLazySchedule, - }) - - lazyVestingSchedule, found = seedAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, lazyVestingSchedule.IsValid()) - - privateAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - privateLazySchedule, - }) - - lazyVestingSchedule, found = privateAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, lazyVestingSchedule.IsValid()) - - employeeAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ - privateBonusLazySchedule, - }) - - lazyVestingSchedule, found = employeeAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) - require.True(t, found) - require.True(t, lazyVestingSchedule.IsValid()) -} diff --git a/types/mock/mockval.go b/types/mock/mockval.go deleted file mode 100644 index 4302cb135..000000000 --- a/types/mock/mockval.go +++ /dev/null @@ -1,65 +0,0 @@ -package mock - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type MockValidator struct { - sdk.Validator - - Address sdk.ValAddress - Power sdk.Int -} - -func NewMockValidator(address sdk.ValAddress, power sdk.Int) MockValidator { - return MockValidator{ - Address: address, - Power: power, - } -} - -func (mv MockValidator) GetBondedTokens() sdk.Int { - return mv.Power -} - -func (mv MockValidator) GetOperator() sdk.ValAddress { - return mv.Address -} - -type MockValset struct { - sdk.ValidatorSet - - Validators []MockValidator -} - -func NewMockValSet() MockValset { - return MockValset{ - Validators: []MockValidator{}, - } -} - -func (mv MockValset) Validator(ctx sdk.Context, valAddress sdk.ValAddress) sdk.Validator { - for _, val := range mv.Validators { - if val.Address.Equals(valAddress) { - return val - } - } - return nil -} - -func (mv MockValset) TotalBondedTokens(ctx sdk.Context) sdk.Int { - rval := sdk.ZeroInt() - for _, val := range mv.Validators { - rval = rval.Add(val.Power) - } - return rval -} - -func (mv MockValset) IterateBondedValidatorsByPower(ctx sdk.Context, - handler func(index int64, validator sdk.Validator) (stop bool)) { - for i, val := range mv.Validators { - if handler(int64(i), val) { - break - } - } -} diff --git a/types/result.go b/types/result.go deleted file mode 100644 index 2ea12fa93..000000000 --- a/types/result.go +++ /dev/null @@ -1,19 +0,0 @@ -package types - -import ( - sdktypes "github.com/cosmos/cosmos-sdk/types" -) - -// TxSearchReponse defines tx search response structure -type TxSearchResponse struct { - Txs []sdktypes.TxResponse `json:"txs"` - TotalCount int `json:"total_count"` -} - -// NewTxSearchResponse returns a TxSearchResponse object -func NewTxSearchResponse(txs []sdktypes.TxResponse, totalCount int) TxSearchResponse { - return TxSearchResponse{ - Txs: txs, - TotalCount: totalCount, - } -} diff --git a/types/util/epoch.go b/types/util/epoch.go index 937fadf1f..9a99cdd8d 100644 --- a/types/util/epoch.go +++ b/types/util/epoch.go @@ -17,9 +17,9 @@ const ( ) // GetEpoch returns the current epoch, starting from 0 -func GetEpoch(ctx sdk.Context) sdk.Int { +func GetEpoch(ctx sdk.Context) int64 { curEpoch := ctx.BlockHeight() / BlocksPerEpoch - return sdk.NewInt(curEpoch) + return curEpoch } // IsPeriodLastBlock returns true if we are at the last block of the period diff --git a/update/end_blocker.go b/update/end_blocker.go deleted file mode 100644 index cf7569033..000000000 --- a/update/end_blocker.go +++ /dev/null @@ -1,27 +0,0 @@ -package update - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/terra-project/core/update/plan" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/oracle" -) - -// EndBlocker -func EndBlocker( - ctx sdk.Context, - accountKeeper auth.AccountKeeper, - oracleKeeper oracle.Keeper, - marketKeeper market.Keeper) (resTags sdk.Tags) { - - if ctx.ChainID() == "columbus-2" { - updated := plan.Update230000(ctx, accountKeeper, oracleKeeper, marketKeeper) - if updated { - resTags.AppendTag(plan.TagUpdate230000, "updated") - } - } - - return -} diff --git a/update/plan/test_common.go b/update/plan/test_common.go deleted file mode 100644 index 619815d8a..000000000 --- a/update/plan/test_common.go +++ /dev/null @@ -1,163 +0,0 @@ -package plan - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - - "time" - - abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - oracleDecPrecision = 8 -) - -func setup(t *testing.T) testInput { - input := createTestInput(t) - - defaultOracleParams := oracle.DefaultParams() - defaultOracleParams.VotePeriod = int64(1) // Set to one block for convinience - input.oracleKeeper.SetParams(input.ctx, defaultOracleParams) - - defaultMarketParams := market.DefaultParams() - defaultMarketParams.DailyLunaDeltaCap = sdk.NewDecWithPrec(5, 3) - input.marketKeeper.SetParams(input.ctx, defaultMarketParams) - - return input -} - -type testInput struct { - ctx sdk.Context - cdc *codec.Codec - accKeeper auth.AccountKeeper - oracleKeeper oracle.Keeper - marketKeeper market.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - types.RegisterCodec(cdc) - oracle.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyMarket := sdk.NewKVStoreKey(market.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingParams := staking.DefaultParams() - stakingParams.BondDenom = assets.MicroLunaDenom - stakingKeeper.SetParams(ctx, stakingParams) - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := market.NewKeeper( - cdc, - keyMarket, - oracleKeeper, - mintKeeper, - paramsKeeper.Subspace(market.DefaultParamspace), - ) - - return testInput{ctx, cdc, accKeeper, oracleKeeper, marketKeeper} -} diff --git a/update/plan/update_230000.go b/update/plan/update_230000.go deleted file mode 100644 index 846859946..000000000 --- a/update/plan/update_230000.go +++ /dev/null @@ -1,275 +0,0 @@ -package plan - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/oracle" -) - -var ( - preseedAddresses = [...]string{ - "terra1p54hc4yy2ajg67j645dn73w3378j6k05v52cnk", - "terra1weyggr6rnggy7vdnp79pxr7vskndegcnswh8gl", - "terra1jd2yth3zl2vez34q4m0wxngaazwy0e8csxuucd", - "terra1upg95nlwkfkrq4hhjrn3k9s6ud0aqx36gwnlsn", - "terra1f2z4q5kdelhfk7xq3xmxzlhp6ntrtzu659pl0s", - } - seedAddresses = [...]string{ - "terra1m8lvnh4zju4zcjh34rjhyyuyjk4m79536jf2tm", - "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6", - "terra13cvpljjkm2huadv38dfx8jyucfda0cs6jp3sku", - "terra1kgj2zc0qxltr5vsgycmxp7x337q5s9lvxlh694", - "terra1zjkr4t3dglt8mzykhtvrq5wltapstlmpdly87u", - "terra1y4umfuqfg76t8mfcff6zzx7elvy93jtp4xcdvw", - } - - // TagUpdate230000 is tag key for update 230000 - TagUpdate230000 = "update_230000" -) - -const ( - // seconds per 30 days - secondsPerMonth = 30 * 24 * 60 * 60 - // columbus-1 genesis time = 2019-04-24T06:00:00.000000Z - genesisUnixTime = 1556085600 -) - -// Update230000 update vesting schedule and oracle param -func Update230000(ctx sdk.Context, accKeeper auth.AccountKeeper, oracleKeeper oracle.Keeper, marketKeeper market.Keeper) bool { - - // check update height - if ctx.BlockHeight() != 230000 { - return false - } - - // update vesting schedule - accKeeper.IterateAccounts(ctx, func(acc auth.Account) (stop bool) { - stop = false - - vacc, ok := acc.(auth.VestingAccount) - - if !ok { - return - } - - gvacc, ok := vacc.(types.GradedVestingAccount) - if ok { - - var lazyVestingSchedules []types.LazyVestingSchedule - - isPreseedAccount := false - for _, addr := range preseedAddresses { - if addr == gvacc.GetAddress().String() { - lazyVestingSchedules = updatePreseedSchedules(gvacc) - isPreseedAccount = true - break - } - } - - isSeedAccount := false - for _, addr := range seedAddresses { - if addr == gvacc.GetAddress().String() { - lazyVestingSchedules = updateSeedSchedules(gvacc) - isSeedAccount = true - break - } - } - - if !isPreseedAccount && !isSeedAccount { - // update to LazyGradedVestingAccount - vestingSchedules := gvacc.GetVestingSchedules() - - for _, vs := range vestingSchedules { - var lazySchedules []types.LazySchedule - for _, s := range vs.Schedules { - lazySchedules = append(lazySchedules, types.NewLazySchedule(s.GetCliff(), s.GetCliff()+secondsPerMonth, s.GetRatio())) - } - - lazyVestingSchedule := types.NewLazyVestingSchedule(vs.GetDenom(), lazySchedules) - lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) - } - } - - for _, lvs := range lazyVestingSchedules { - if !lvs.IsValid() { - panic(fmt.Sprintf("not valid schdule: %v\n %v", gvacc, lvs)) - } - } - - baseAccount := &auth.BaseAccount{ - Address: gvacc.GetAddress(), - PubKey: gvacc.GetPubKey(), - Coins: gvacc.GetCoins().Sort(), - AccountNumber: gvacc.GetAccountNumber(), - Sequence: gvacc.GetSequence(), - } - - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: baseAccount, - OriginalVesting: gvacc.GetOriginalVesting(), - DelegatedFree: gvacc.GetDelegatedFree(), - DelegatedVesting: gvacc.GetDelegatedVesting(), - EndTime: gvacc.GetEndTime(), - } - - lazyAccount := types.BaseLazyGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - LazyVestingSchedules: lazyVestingSchedules, - } - - accKeeper.SetAccount(ctx, lazyAccount) - } - - return - }) - - // update oracle reward band param - oracleParams := oracleKeeper.GetParams(ctx) - oracleParams.OracleRewardBand = sdk.NewDecWithPrec(2, 2) // 2% - oracleKeeper.SetParams(ctx, oracleParams) - - // update market swap delta limit param - marketParams := marketKeeper.GetParams(ctx) - marketParams.DailyLunaDeltaCap = sdk.NewDecWithPrec(1, 3) // 0.1% - marketKeeper.SetParams(ctx, marketParams) - - return true -} - -// preseed account does not have any other schedules -func updatePreseedSchedules(gvacc types.GradedVestingAccount) []types.LazyVestingSchedule { - - vestingSchedules := gvacc.GetVestingSchedules() - if len(vestingSchedules) != 1 || vestingSchedules[0].GetDenom() != assets.MicroLunaDenom { - panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) - } - - vestingSchedule := vestingSchedules[0] - if len(vestingSchedule.Schedules) != 4 { - panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) - } - - // strict preseed account check - for _, s := range vestingSchedule.Schedules { - if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if s.GetCliff() == 1587708000 && s.GetRatio().Equal(sdk.NewDecWithPrec(70, 2)) { - continue - } else { - panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) - } - } - - var lazyVestingSchedules []types.LazyVestingSchedule - var lazyVestingSchedule types.LazyVestingSchedule - var lazySchedules []types.LazySchedule - - genesisTime := time.Unix(genesisUnixTime, 0).UTC() - lazySchedules = append(lazySchedules, - types.NewLazySchedule( - genesisTime.AddDate(0, 1, 0).Unix(), genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewLazySchedule( - genesisTime.AddDate(0, 2, 0).Unix(), genesisTime.AddDate(0, 12, 0).Unix(), sdk.NewDecWithPrec(27, 2)), - types.NewLazySchedule( - genesisTime.AddDate(0, 12, 0).Unix(), genesisTime.AddDate(0, 17, 0).Unix(), sdk.NewDecWithPrec(48, 2)), - types.NewLazySchedule( - genesisTime.AddDate(0, 17, 0).Unix(), genesisTime.AddDate(0, 18, 0).Unix(), sdk.NewDecWithPrec(15, 2)), - ) - - lazyVestingSchedule = types.NewLazyVestingSchedule(assets.MicroLunaDenom, lazySchedules) - lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) - - return lazyVestingSchedules -} - -// only terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6 account has difference vesting schedule -// except that all seed account does not have any other schedules -func updateSeedSchedules(gvacc types.GradedVestingAccount) []types.LazyVestingSchedule { - vestingSchedules := gvacc.GetVestingSchedules() - if len(vestingSchedules) != 1 || vestingSchedules[0].GetDenom() != assets.MicroLunaDenom { - panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) - } - - vestingSchedule := vestingSchedules[0] - ratio := sdk.OneDec() - - // strict seed account check - if gvacc.GetAddress().String() == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { - ratio = sdk.NewDecWithPrec(467, 3) - - if len(vestingSchedule.Schedules) != 5 { - panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) - } - - for _, s := range vestingSchedule.Schedules { - if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { - continue - } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { - continue - } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { - continue - } else if s.GetCliff() == 1582524000 && s.GetRatio().Equal(sdk.NewDecWithPrec(326, 3)) { - continue - } else if s.GetCliff() == 1603519200 && s.GetRatio().Equal(sdk.NewDecWithPrec(533, 3)) { - continue - } else { - panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) - } - } - } else { - if len(vestingSchedule.Schedules) != 4 { - panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) - } - - for _, s := range vestingSchedule.Schedules { - if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { - continue - } else if (s.GetCliff() == 1582524000 || s.GetCliff() == 1579845600) && s.GetRatio().Equal(sdk.NewDecWithPrec(70, 2)) { - continue - } else { - panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) - } - } - } - - var lazyVestingSchedules []types.LazyVestingSchedule - var lazyVestingSchedule types.LazyVestingSchedule - var lazySchedules []types.LazySchedule - - genesisTime := time.Unix(genesisUnixTime, 0).UTC() - lazySchedules = append(lazySchedules, - types.NewLazySchedule( - genesisTime.AddDate(0, 1, 0).Unix(), genesisTime.AddDate(0, 2, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(10, 2))), - types.NewLazySchedule( - genesisTime.AddDate(0, 2, 0).Unix(), genesisTime.AddDate(0, 10, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(30, 2))), - types.NewLazySchedule( - genesisTime.AddDate(0, 10, 0).Unix(), genesisTime.AddDate(0, 13, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(60, 2))), - ) - - if !ratio.Equal(sdk.OneDec()) { - lazySchedules = append(lazySchedules, - types.NewLazySchedule( - genesisTime.AddDate(0, 18, 0).Unix(), genesisTime.AddDate(0, 19, 0).Unix(), sdk.OneDec().Sub(ratio)), - ) - } - - lazyVestingSchedule = types.NewLazyVestingSchedule(assets.MicroLunaDenom, lazySchedules) - lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) - - return lazyVestingSchedules -} diff --git a/update/plan/update_230000_test.go b/update/plan/update_230000_test.go deleted file mode 100644 index 09e43c582..000000000 --- a/update/plan/update_230000_test.go +++ /dev/null @@ -1,376 +0,0 @@ -package plan - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" -) - -var ( - genesisTime = time.Unix(1556085600, 0).UTC() - - preseedSchedule types.VestingSchedule - seedSchedule types.VestingSchedule - seedSchedule2 types.VestingSchedule - privateSchedule types.VestingSchedule - - preseedAccounts []types.BaseGradedVestingAccount - seedAccounts []types.BaseGradedVestingAccount - normalAccounts []types.BaseGradedVestingAccount - - normalAddress = [...]string{ - "terra1wpplgwx5q2ph7z2vqm9m0t2jgr6qyjkwhxvff3", - "terra1dp0taj85ruc299rkdvzp4z5pfg6z6swaed74e6", - } - - preseedCoins = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(79411554440)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(3393296)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(20000000000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(2000007013377)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(13015)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(401836579451)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(833906)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4176551929047)), - ), - } - - preseedOriginalVesting = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(30000000000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(20000000000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(10000000000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(5000000000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4500000000000)), - ), - } - - seedCoins = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(1729185508867)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1753376)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(9347826000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3913044896495)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(7373288)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(59867)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3826087000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3913044998248)), - ), - } - - seedOriginalVesting = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(13043478000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(9347826000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3826087000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), - ), - } - - normalCoins = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(350000000000)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(62500000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(695561902462109)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(999916140381807)), - ), - } - - normalOriginalVesting = []sdk.Coins{ - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(350000000000)), - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(62500000000)), - ), - sdk.NewCoins( - sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(1000000000000000)), - ), - } -) - -func init() { - - config := sdk.GetConfig() - config.SetCoinType(330) - config.SetFullFundraiserPath("44'/330'/0'/0/0") - config.SetBech32PrefixForAccount(util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub) - config.SetBech32PrefixForValidator(util.Bech32PrefixValAddr, util.Bech32PrefixValPub) - config.SetBech32PrefixForConsensusNode(util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub) - config.Seal() - - preseedSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ - types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 12, 0).Unix(), sdk.NewDecWithPrec(70, 2)), - }) - - seedSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ - types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(10, 2)), - types.NewSchedule(genesisTime.AddDate(0, 10, 0).Unix(), sdk.NewDecWithPrec(70, 2)), - }) - - seedSchedule2 = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ - types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(47, 3)), - types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(47, 3)), - types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(47, 3)), - types.NewSchedule(genesisTime.AddDate(0, 10, 0).Unix(), sdk.NewDecWithPrec(326, 3)), - types.NewSchedule(genesisTime.AddDate(0, 18, 0).Unix(), sdk.NewDecWithPrec(533, 3)), - }) - - privateSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ - types.NewSchedule(genesisTime.AddDate(0, 4, 0).Unix(), sdk.NewDecWithPrec(16, 2)), - types.NewSchedule(genesisTime.AddDate(0, 5, 0).Unix(), sdk.NewDecWithPrec(16, 2)), - types.NewSchedule(genesisTime.AddDate(0, 6, 0).Unix(), sdk.NewDecWithPrec(16, 2)), - types.NewSchedule(genesisTime.AddDate(0, 7, 0).Unix(), sdk.NewDecWithPrec(16, 2)), - types.NewSchedule(genesisTime.AddDate(0, 8, 0).Unix(), sdk.NewDecWithPrec(16, 2)), - types.NewSchedule(genesisTime.AddDate(0, 9, 0).Unix(), sdk.NewDecWithPrec(20, 2)), - }) - - for i, bechAddr := range preseedAddresses { - addr, _ := sdk.AccAddressFromBech32(bechAddr) - baseAccount := &auth.BaseAccount{ - Address: addr, - Coins: preseedCoins[i], - } - - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: baseAccount, - OriginalVesting: preseedOriginalVesting[i], - } - - gradedVestingAccount := types.BaseGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - VestingSchedules: []types.VestingSchedule{preseedSchedule}, - } - - preseedAccounts = append(preseedAccounts, gradedVestingAccount) - } - - for i, bechAddr := range seedAddresses { - addr, _ := sdk.AccAddressFromBech32(bechAddr) - - baseAccount := &auth.BaseAccount{ - Address: addr, - Coins: seedCoins[i], - } - - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: baseAccount, - OriginalVesting: seedOriginalVesting[i], - } - - gradedVestingAccount := types.BaseGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - VestingSchedules: []types.VestingSchedule{seedSchedule}, - } - - if bechAddr == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { - gradedVestingAccount.VestingSchedules = []types.VestingSchedule{seedSchedule2} - } - - seedAccounts = append(seedAccounts, gradedVestingAccount) - } - - for i, bechAddr := range normalAddress { - addr, _ := sdk.AccAddressFromBech32(bechAddr) - - baseAccount := &auth.BaseAccount{ - Address: addr, - Coins: normalCoins[i], - } - - baseVestingAcc := &auth.BaseVestingAccount{ - BaseAccount: baseAccount, - OriginalVesting: normalOriginalVesting[i], - } - - gradedVestingAccount := types.BaseGradedVestingAccount{ - BaseVestingAccount: baseVestingAcc, - VestingSchedules: []types.VestingSchedule{privateSchedule}, - } - - normalAccounts = append(normalAccounts, gradedVestingAccount) - } -} - -func TestTimeZone(t *testing.T) { - genesisTime := time.Unix(1556085600, 0) - location, _ := time.LoadLocation("Europe/Budapest") - genesisTime = genesisTime.In(location) - result := genesisTime.AddDate(0, 10, 0).Unix() - require.NotEqual(t, int64(1582524000), result) - - genesisTime = genesisTime.UTC() - result = genesisTime.AddDate(0, 10, 0).Unix() - require.Equal(t, int64(1582524000), result) -} - -func TestPreseedAccountUpdate(t *testing.T) { - - for _, acc := range preseedAccounts { - lazyVestingSchedules := updatePreseedSchedules(&acc) - - require.Equal(t, 1, len(lazyVestingSchedules)) - require.Equal(t, assets.MicroLunaDenom, lazyVestingSchedules[0].GetDenom()) - lazySchedule := lazyVestingSchedules[0].LazySchedules - - require.Equal(t, genesisTime.AddDate(0, 1, 0).Unix(), lazySchedule[0].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[0].GetEndTime()) - require.Equal(t, sdk.NewDecWithPrec(10, 2), lazySchedule[0].GetRatio()) - - require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[1].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 12, 0).Unix(), lazySchedule[1].GetEndTime()) - require.Equal(t, sdk.NewDecWithPrec(27, 2), lazySchedule[1].GetRatio()) - - require.Equal(t, genesisTime.AddDate(0, 12, 0).Unix(), lazySchedule[2].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 17, 0).Unix(), lazySchedule[2].GetEndTime()) - require.Equal(t, sdk.NewDecWithPrec(48, 2), lazySchedule[2].GetRatio()) - - require.Equal(t, genesisTime.AddDate(0, 17, 0).Unix(), lazySchedule[3].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 18, 0).Unix(), lazySchedule[3].GetEndTime()) - require.Equal(t, sdk.NewDecWithPrec(15, 2), lazySchedule[3].GetRatio()) - - require.True(t, lazyVestingSchedules[0].IsValid()) - } - -} - -func TestSeedAccountUpdate(t *testing.T) { - - for _, acc := range seedAccounts { - lazyVestingSchedules := updateSeedSchedules(&acc) - - ratio := sdk.OneDec() - - if acc.GetAddress().String() == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { - ratio = sdk.NewDecWithPrec(467, 3) - } - - require.Equal(t, 1, len(lazyVestingSchedules)) - require.Equal(t, assets.MicroLunaDenom, lazyVestingSchedules[0].GetDenom()) - lazySchedule := lazyVestingSchedules[0].LazySchedules - - require.Equal(t, genesisTime.AddDate(0, 1, 0).Unix(), lazySchedule[0].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[0].GetEndTime()) - require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(10, 2)), lazySchedule[0].GetRatio()) - - require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[1].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 10, 0).Unix(), lazySchedule[1].GetEndTime()) - require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(30, 2)), lazySchedule[1].GetRatio()) - - require.Equal(t, genesisTime.AddDate(0, 10, 0).Unix(), lazySchedule[2].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 13, 0).Unix(), lazySchedule[2].GetEndTime()) - require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(60, 2)), lazySchedule[2].GetRatio()) - - if !ratio.Equal(sdk.OneDec()) { - require.Equal(t, genesisTime.AddDate(0, 18, 0).Unix(), lazySchedule[3].GetStartTime()) - require.Equal(t, genesisTime.AddDate(0, 19, 0).Unix(), lazySchedule[3].GetEndTime()) - require.Equal(t, sdk.OneDec().Sub(ratio), lazySchedule[3].GetRatio()) - } - - require.True(t, lazyVestingSchedules[0].IsValid()) - } - -} - -func TestUpdate230000(t *testing.T) { - input := setup(t) - - for _, acc := range preseedAccounts { - input.accKeeper.SetAccount(input.ctx, acc) - } - - for _, acc := range seedAccounts { - input.accKeeper.SetAccount(input.ctx, acc) - } - - for _, acc := range normalAccounts { - input.accKeeper.SetAccount(input.ctx, acc) - } - - updated := Update230000(input.ctx.WithBlockHeight(229999), input.accKeeper, input.oracleKeeper, input.marketKeeper) - require.Equal(t, sdk.NewDecWithPrec(1, 2), input.oracleKeeper.GetParams(input.ctx).OracleRewardBand) - require.Equal(t, sdk.NewDecWithPrec(5, 3), input.marketKeeper.GetParams(input.ctx).DailyLunaDeltaCap) - require.False(t, updated) - - // not yet changed - input.accKeeper.IterateAccounts(input.ctx, func(acc auth.Account) (stop bool) { - stop = false - - vacc, ok := acc.(auth.VestingAccount) - require.True(t, ok) - - _, ok = vacc.(types.GradedVestingAccount) - require.True(t, ok) - return - }) - - updated = Update230000(input.ctx.WithBlockHeight(230000), input.accKeeper, input.oracleKeeper, input.marketKeeper) - require.Equal(t, sdk.NewDecWithPrec(2, 2), input.oracleKeeper.GetParams(input.ctx).OracleRewardBand) - require.Equal(t, sdk.NewDecWithPrec(1, 3), input.marketKeeper.GetParams(input.ctx).DailyLunaDeltaCap) - require.True(t, updated) - - // not yet changed - input.accKeeper.IterateAccounts(input.ctx, func(acc auth.Account) (stop bool) { - stop = false - - vacc, ok := acc.(auth.VestingAccount) - require.True(t, ok) - - _, ok = vacc.(types.LazyGradedVestingAccount) - require.True(t, ok) - return - }) - -} diff --git a/version/command.go b/version/command.go deleted file mode 100644 index e9e9a1de3..000000000 --- a/version/command.go +++ /dev/null @@ -1,48 +0,0 @@ -package version - -import ( - "encoding/json" - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/tendermint/libs/cli" -) - -const ( - flagLong = "long" -) - -var ( - - // VersionCmd prints out the current sdk version - VersionCmd = &cobra.Command{ - Use: "version", - Short: "Print the app version", - RunE: func(_ *cobra.Command, _ []string) error { - verInfo := newVersionInfo() - - if !viper.GetBool(flagLong) { - fmt.Println(verInfo.Core) - return nil - } - - if viper.GetString(cli.OutputFlag) != "json" { - fmt.Print(verInfo) - return nil - } - - bz, err := json.Marshal(verInfo) - if err != nil { - return err - } - fmt.Println(string(bz)) - return nil - }, - } -) - -func init() { - VersionCmd.Flags().Bool(flagLong, false, "Print long version information") -} diff --git a/version/version.go b/version/version.go deleted file mode 100644 index df15c684e..000000000 --- a/version/version.go +++ /dev/null @@ -1,40 +0,0 @@ -//nolint -package version - -import ( - "fmt" - "runtime" -) - -// Variables set by build flags -var ( - Commit = "" - Version = "" - GoSumHash = "" - BuildTags = "" -) - -type versionInfo struct { - Core string `json:"core"` - GitCommit string `json:"commit"` - GoSumHash string `json:"gosum_hash"` - BuildTags string `json:"build_tags"` - GoVersion string `json:"go"` -} - -func (v versionInfo) String() string { - return fmt.Sprintf(`core: %s -git commit: %s -go.sum hash: %s -build tags: %s -%s`, v.Core, v.GitCommit, v.GoSumHash, v.BuildTags, v.GoVersion) -} - -func newVersionInfo() versionInfo { - return versionInfo{ - Version, - Commit, - GoSumHash, - BuildTags, - fmt.Sprintf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)} -} diff --git a/x/auth/alias.go b/x/auth/alias.go new file mode 100644 index 000000000..3dc5cb408 --- /dev/null +++ b/x/auth/alias.go @@ -0,0 +1,30 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/auth/internal/types/ +package auth + +import ( + "github.com/terra-project/core/x/auth/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + NewLazySchedule = types.NewLazySchedule + NewVestingSchedule = types.NewVestingSchedule + NewBaseLazyGradedVestingAccountRaw = types.NewBaseLazyGradedVestingAccountRaw + NewBaseLazyGradedVestingAccount = types.NewBaseLazyGradedVestingAccount + + // variable aliases + ModuleCdc = types.ModuleCdc +) + +type ( + LazySchedule = types.LazySchedule + VestingSchedule = types.VestingSchedule + LazyGradedVestingAccount = types.LazyGradedVestingAccount + BaseLazyGradedVestingAccount = types.BaseLazyGradedVestingAccount + TreasuryKeeper = types.TreasuryKeeper + SupplyKeeper = types.SupplyKeeper +) diff --git a/x/auth/ante.go b/x/auth/ante.go new file mode 100644 index 000000000..3ea0876d8 --- /dev/null +++ b/x/auth/ante.go @@ -0,0 +1,497 @@ +package auth + +import ( + "bytes" + "encoding/hex" + "fmt" + + "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/multisig" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/bank" +) + +var ( + // simulation signature values used to estimate gas consumption + simSecp256k1Pubkey secp256k1.PubKeySecp256k1 + simSecp256k1Sig [64]byte +) + +func init() { + // This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation + bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") + copy(simSecp256k1Pubkey[:], bz) +} + +// SignatureVerificationGasConsumer is the type of function that is used to both consume gas when verifying signatures +// and also to accept or reject different types of PubKey's. This is where apps can define their own PubKey +type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result + +// NewAnteHandler returns an AnteHandler that checks and increments sequence +// numbers, checks signatures & account numbers, and deducts fees from the first +// signer. +func NewAnteHandler(ak AccountKeeper, supplyKeeper types.SupplyKeeper, + treasuryKeeper TreasuryKeeper, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler { + return func( + ctx sdk.Context, tx sdk.Tx, simulate bool, + ) (newCtx sdk.Context, res sdk.Result, abort bool) { + + if addr := supplyKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName)) + } + + // all transactions must be of type StdTx + stdTx, ok := tx.(StdTx) + if !ok { + // Set a gas meter with limit 0 as to prevent an infinite gas meter attack + // during runTx. + newCtx = SetGasMeter(simulate, ctx, 0) + return newCtx, sdk.ErrInternal("tx must be StdTx").Result(), true + } + + params := ak.GetParams(ctx) + taxes := filterMsgAndComputeTax(ctx, treasuryKeeper, stdTx.GetMsgs()) + + // Ensure given fee has enough amount to cover taxes + if !simulate { + if _, hasNeg := stdTx.Fee.Amount.SafeSub(taxes); hasNeg { + return newCtx, sdk.ErrInsufficientFee( + fmt.Sprintf("insufficient fees to pay for taxes; %s < %s", stdTx.Fee.Amount, taxes), + ).Result(), true + } + } + + // Ensure that the provided fees meet a minimum threshold for the validator, + // if this is a CheckTx. This is only for local mempool purposes, and thus + // is only ran on check tx. + if ctx.IsCheckTx() && !simulate { + res := EnsureSufficientMempoolFees(ctx, stdTx.Fee, taxes) + if !res.IsOK() { + return newCtx, res, true + } + } + + newCtx = SetGasMeter(simulate, ctx, stdTx.Fee.Gas) + + // AnteHandlers must have their own defer/recover in order for the BaseApp + // to know how much gas was used! This is because the GasMeter is created in + // the AnteHandler, but if it panics the context won't be set properly in + // runTx's recover call. + defer func() { + if r := recover(); r != nil { + switch rType := r.(type) { + case sdk.ErrorOutOfGas: + log := fmt.Sprintf( + "out of gas in location: %v; gasWanted: %d, gasUsed: %d", + rType.Descriptor, stdTx.Fee.Gas, newCtx.GasMeter().GasConsumed(), + ) + res = sdk.ErrOutOfGas(log).Result() + + res.GasWanted = stdTx.Fee.Gas + res.GasUsed = newCtx.GasMeter().GasConsumed() + abort = true + default: + panic(r) + } + } + }() + + if res := ValidateSigCount(stdTx, params); !res.IsOK() { + return newCtx, res, true + } + + if err := tx.ValidateBasic(); err != nil { + return newCtx, err.Result(), true + } + + newCtx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*sdk.Gas(len(newCtx.TxBytes())), "txSize") + + if res := ValidateMemo(stdTx, params); !res.IsOK() { + return newCtx, res, true + } + + // stdSigs contains the sequence number, account number, and signatures. + // When simulating, this would just be a 0-length slice. + signerAddrs := stdTx.GetSigners() + signerAccs := make([]Account, len(signerAddrs)) + isGenesis := ctx.BlockHeight() == 0 + + // fetch first signer, who's going to pay the fees + signerAccs[0], res = GetSignerAcc(newCtx, ak, signerAddrs[0]) + if !res.IsOK() { + return newCtx, res, true + } + + // deduct the fees + if !stdTx.Fee.Amount.IsZero() { + res = DeductFees(supplyKeeper, newCtx, signerAccs[0], stdTx.Fee.Amount) + if !res.IsOK() { + return newCtx, res, true + } + + // record tax proceeds + treasuryKeeper.RecordTaxProceeds(newCtx, stdTx.Fee.Amount) + + // reload the account as fees have been deducted + signerAccs[0] = ak.GetAccount(newCtx, signerAccs[0].GetAddress()) + } + + // stdSigs contains the sequence number, account number, and signatures. + // When simulating, this would just be a 0-length slice. + stdSigs := stdTx.GetSignatures() + + for i := 0; i < len(stdSigs); i++ { + // skip the fee payer, account is cached and fees were deducted already + if i != 0 { + signerAccs[i], res = GetSignerAcc(newCtx, ak, signerAddrs[i]) + if !res.IsOK() { + return newCtx, res, true + } + } + + // check signature, return account with incremented nonce + signBytes := GetSignBytes(newCtx.ChainID(), stdTx, signerAccs[i], isGenesis) + signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytes, simulate, params, sigGasConsumer) + if !res.IsOK() { + return newCtx, res, true + } + + ak.SetAccount(newCtx, signerAccs[i]) + } + + // TODO: tx tags (?) + return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false // continue... + } +} + +// GetSignerAcc returns an account for a given address that is expected to sign +// a transaction. +func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) (Account, sdk.Result) { + if acc := ak.GetAccount(ctx, addr); acc != nil { + return acc, sdk.Result{} + } + return nil, sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr)).Result() +} + +// ValidateSigCount validates that the transaction has a valid cumulative total +// amount of signatures. +func ValidateSigCount(stdTx StdTx, params Params) sdk.Result { + stdSigs := stdTx.GetSignatures() + + sigCount := 0 + for i := 0; i < len(stdSigs); i++ { + sigCount += CountSubKeys(stdSigs[i].PubKey) + if uint64(sigCount) > params.TxSigLimit { + return sdk.ErrTooManySignatures( + fmt.Sprintf("signatures: %d, limit: %d", sigCount, params.TxSigLimit), + ).Result() + } + } + + return sdk.Result{} +} + +// ValidateMemo validates the memo size. +func ValidateMemo(stdTx StdTx, params Params) sdk.Result { + memoLength := len(stdTx.GetMemo()) + if uint64(memoLength) > params.MaxMemoCharacters { + return sdk.ErrMemoTooLarge( + fmt.Sprintf( + "maximum number of characters is %d but received %d characters", + params.MaxMemoCharacters, memoLength, + ), + ).Result() + } + + return sdk.Result{} +} + +// verify the signature and increment the sequence. If the account doesn't have +// a pubkey, set it. +func processSig( + ctx sdk.Context, acc Account, sig StdSignature, signBytes []byte, simulate bool, params Params, + sigGasConsumer SignatureVerificationGasConsumer, +) (updatedAcc Account, res sdk.Result) { + + pubKey, res := ProcessPubKey(acc, sig, simulate) + if !res.IsOK() { + return nil, res + } + + err := acc.SetPubKey(pubKey) + if err != nil { + return nil, sdk.ErrInternal("setting PubKey on signer's account").Result() + } + + if simulate { + // Simulated txs should not contain a signature and are not required to + // contain a pubkey, so we must account for tx size of including a + // StdSignature (Amino encoding) and simulate gas consumption + // (assuming a SECP256k1 simulation key). + consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params) + } + + if res := sigGasConsumer(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() { + return nil, res + } + + if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) { + return nil, sdk.ErrUnauthorized("signature verification failed; verify correct account sequence and chain-id").Result() + } + + if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { + panic(err) + } + + return acc, res +} + +func consumeSimSigGas(gasmeter sdk.GasMeter, pubkey crypto.PubKey, sig StdSignature, params Params) { + simSig := StdSignature{PubKey: pubkey} + if len(sig.Signature) == 0 { + simSig.Signature = simSecp256k1Sig[:] + } + + sigBz := ModuleCdc.MustMarshalBinaryLengthPrefixed(simSig) + cost := sdk.Gas(len(sigBz) + 6) + + // If the pubkey is a multi-signature pubkey, then we estimate for the maximum + // number of signers. + if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok { + cost *= params.TxSigLimit + } + + gasmeter.ConsumeGas(params.TxSizeCostPerByte*cost, "txSize") +} + +// ProcessPubKey verifies that the given account address matches that of the +// StdSignature. In addition, it will set the public key of the account if it +// has not been set. +func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, sdk.Result) { + // If pubkey is not known for account, set it from the StdSignature. + pubKey := acc.GetPubKey() + if simulate { + // In simulate mode the transaction comes with no signatures, thus if the + // account's pubkey is nil, both signature verification and gasKVStore.Set() + // shall consume the largest amount, i.e. it takes more gas to verify + // secp256k1 keys than ed25519 ones. + if pubKey == nil { + return simSecp256k1Pubkey, sdk.Result{} + } + + return pubKey, sdk.Result{} + } + + if pubKey == nil { + pubKey = sig.PubKey + if pubKey == nil { + return nil, sdk.ErrInvalidPubKey("PubKey not found").Result() + } + + if !bytes.Equal(pubKey.Address(), acc.GetAddress()) { + return nil, sdk.ErrInvalidPubKey( + fmt.Sprintf("PubKey does not match Signer address %s", acc.GetAddress())).Result() + } + } + + return pubKey, sdk.Result{} +} + +// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas +// for signature verification based upon the public key type. The cost is fetched from the given params and is matched +// by the concrete type. +func DefaultSigVerificationGasConsumer( + meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params, +) sdk.Result { + switch pubkey := pubkey.(type) { + case ed25519.PubKeyEd25519: + meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") + return sdk.ErrInvalidPubKey("ED25519 public keys are unsupported").Result() + + case secp256k1.PubKeySecp256k1: + meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") + return sdk.Result{} + + case multisig.PubKeyMultisigThreshold: + var multisignature multisig.Multisignature + codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature) + + consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params) + return sdk.Result{} + + default: + return sdk.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %T", pubkey)).Result() + } +} + +func consumeMultisignatureVerificationGas(meter sdk.GasMeter, + sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold, + params Params) { + + size := sig.BitArray.Size() + sigIndex := 0 + for i := 0; i < size; i++ { + if sig.BitArray.GetIndex(i) { + DefaultSigVerificationGasConsumer(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params) + sigIndex++ + } + } +} + +// DeductFees deducts fees from the given account. +// +// NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because +// the CoinKeeper doesn't give us accounts), but it seems easier to do this. +func DeductFees(supplyKeeper types.SupplyKeeper, ctx sdk.Context, acc Account, fees sdk.Coins) sdk.Result { + blockTime := ctx.BlockHeader().Time + coins := acc.GetCoins() + + if !fees.IsValid() { + return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", fees)).Result() + } + + // verify the account has enough funds to pay for fees + _, hasNeg := coins.SafeSub(fees) + if hasNeg { + return sdk.ErrInsufficientFunds( + fmt.Sprintf("insufficient funds to pay for fees; %s < %s", coins, fees), + ).Result() + } + + // Validate the account has enough "spendable" coins as this will cover cases + // such as vesting accounts. + spendableCoins := acc.SpendableCoins(blockTime) + if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { + return sdk.ErrInsufficientFunds( + fmt.Sprintf("insufficient funds to pay for fees; %s < %s", spendableCoins, fees), + ).Result() + } + + err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) + if err != nil { + return err.Result() + } + + return sdk.Result{} +} + +// filterMsgAndComputeTax computes the stability tax on MsgSend and MsgMultiSend. +func filterMsgAndComputeTax(ctx sdk.Context, tk TreasuryKeeper, msgs []sdk.Msg) (taxes sdk.Coins) { + for _, msg := range msgs { + switch msg := msg.(type) { + case bank.MsgSend: + taxes = taxes.Add(computeTax(ctx, tk, msg.Amount)) + + case bank.MsgMultiSend: + for _, input := range msg.Inputs { + taxes = taxes.Add(computeTax(ctx, tk, input.Coins)) + } + } + } + + return +} + +// computes the stability tax according to tax-rate and tax-cap +func computeTax(ctx sdk.Context, tk TreasuryKeeper, principal sdk.Coins) (taxes sdk.Coins) { + taxRate := tk.GetTaxRate(ctx, core.GetEpoch(ctx)) + if taxRate.Equal(sdk.ZeroDec()) { + return + } + + for _, coin := range principal { + if coin.Denom == core.MicroLunaDenom { + continue + } + + taxDue := sdk.NewDecFromInt(coin.Amount).Mul(taxRate).TruncateInt() + + // If tax due is greater than the tax cap, cap! + taxCap := tk.GetTaxCap(ctx, coin.Denom) + if taxDue.GT(taxCap) { + taxDue = taxCap + } + + if taxDue.Equal(sdk.ZeroInt()) { + continue + } + + taxes = taxes.Add(sdk.NewCoins(sdk.NewCoin(coin.Denom, taxDue))) + } + + return +} + +// EnsureSufficientMempoolFees verifies that the given transaction has supplied +// enough fees(gas + stability) to cover a proposer's minimum fees. A result object is returned +// indicating success or failure. +// +// Contract: This should only be called during CheckTx as it cannot be part of +// consensus. +func EnsureSufficientMempoolFees(ctx sdk.Context, stdFee StdFee, taxes sdk.Coins) sdk.Result { + var requiredFees sdk.Coins + minGasPrices := ctx.MinGasPrices() + if !minGasPrices.IsZero() { + requiredFees = make(sdk.Coins, len(minGasPrices)) + + // Determine the required fees by multiplying each required minimum gas + // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). + glDec := sdk.NewDec(int64(stdFee.Gas)) + for i, gp := range minGasPrices { + fee := gp.Amount.Mul(glDec) + requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + } + + // Before checking gas prices, remove taxed from fee + var hasNeg bool + if stdFee.Amount, hasNeg = stdFee.Amount.SafeSub(taxes); hasNeg { + return sdk.ErrInsufficientFee( + fmt.Sprintf( + "insufficient fees; got: %q, required: %q = %q(gas) +%q(stability)", stdFee.Amount.Add(taxes), requiredFees.Add(taxes), requiredFees, taxes, + ), + ).Result() + } + + if !requiredFees.Empty() && !stdFee.Amount.IsAnyGTE(requiredFees) { + return sdk.ErrInsufficientFee( + fmt.Sprintf( + "insufficient fees; got: %q, required: %q = %q(gas) +%q(stability)", stdFee.Amount.Add(taxes), requiredFees.Add(taxes), requiredFees, taxes, + ), + ).Result() + } + + return sdk.Result{} +} + +// SetGasMeter returns a new context with a gas meter set from a given context. +func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit uint64) sdk.Context { + // In various cases such as simulation and during the genesis block, we do not + // meter any gas utilization. + if simulate || ctx.BlockHeight() == 0 { + return ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + } + + return ctx.WithGasMeter(sdk.NewGasMeter(gasLimit)) +} + +// GetSignBytes returns a slice of bytes to sign over for a given transaction +// and an account. +func GetSignBytes(chainID string, stdTx StdTx, acc Account, genesis bool) []byte { + var accNum uint64 + if !genesis { + accNum = acc.GetAccountNumber() + } + + return StdSignBytes( + chainID, accNum, acc.GetSequence(), stdTx.Fee, stdTx.Msgs, stdTx.Memo, + ) +} diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go new file mode 100644 index 000000000..03d33eea9 --- /dev/null +++ b/x/auth/ante_test.go @@ -0,0 +1,831 @@ +package auth + +import ( + "fmt" + "math/rand" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/multisig" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/bank" +) + +// run the tx through the anteHandler and ensure its valid +func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) { + _, result, abort := anteHandler(ctx, tx, simulate) + require.Equal(t, "", result.Log) + require.False(t, abort) + require.Equal(t, sdk.CodeOK, result.Code) + require.True(t, result.IsOK()) +} + +// run the tx through the anteHandler and ensure it fails with the given code +func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool, code sdk.CodeType) { + newCtx, result, abort := anteHandler(ctx, tx, simulate) + require.True(t, abort) + + require.Equal(t, code, result.Code, fmt.Sprintf("Expected %v, got %v", code, result)) + require.Equal(t, sdk.CodespaceRoot, result.Codespace) + + if code == sdk.CodeOutOfGas { + stdTx, ok := tx.(StdTx) + require.True(t, ok, "tx must be in form StdTx") + // GasWanted set correctly + require.Equal(t, stdTx.Fee.Gas, result.GasWanted, "Gas wanted not set correctly") + require.True(t, result.GasUsed > result.GasWanted, "GasUsed not greated than GasWanted") + // Check that context is set correctly + require.Equal(t, result.GasUsed, newCtx.GasMeter().GasConsumed(), "Context not updated correctly") + } +} + +// Test various error cases in the AnteHandler control flow. +func TestAnteHandlerSigErrors(t *testing.T) { + // setup + input := setupTestInput() + ctx := input.ctx + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + priv3, _, addr3 := types.KeyTestPubAddr() + + // msg and signatures + var tx sdk.Tx + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr1, addr3) + fee := types.NewTestStdFee() + + msgs := []sdk.Msg{msg1, msg2} + + // test no signatures + privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} + tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + + // tx.GetSigners returns addresses in correct order: addr1, addr2, addr3 + expectedSigners := []sdk.AccAddress{addr1, addr2, addr3} + stdTx := tx.(StdTx) + require.Equal(t, expectedSigners, stdTx.GetSigners()) + + // Check no signatures fails + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeNoSignatures) + + // test num sigs dont match GetSigners + privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // test an unrecognized account + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) + + // save the first account, but second is still unrecognized + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(fee.Amount) + input.ak.SetAccount(ctx, acc1) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) +} + +// Test logic around account number checking with one signer and many signers. +func TestAnteHandlerAccountNumbers(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + + // msg and signatures + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + fee := types.NewTestStdFee() + + msgs := []sdk.Msg{msg} + + // test good tx from one signer + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx from wrong account number + seqs = []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // from correct account number + seqs = []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx with another signer and incorrect account numbers + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr2, addr1) + msgs = []sdk.Msg{msg1, msg2} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // correct account numbers + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{2, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) +} + +// Test logic around account number checking with many signers when BlockHeight is 0. +func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(0) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + + // set the accounts, we don't need the acc numbers as it is in the genesis block + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + + // msg and signatures + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + fee := types.NewTestStdFee() + + msgs := []sdk.Msg{msg} + + // test good tx from one signer + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx from wrong account number + seqs = []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // from correct account number + seqs = []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx with another signer and incorrect account numbers + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr2, addr1) + msgs = []sdk.Msg{msg1, msg2} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // correct account numbers + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{2, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) +} + +// Test logic around sequence checking with one signer and many signers. +func TestAnteHandlerSequences(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + priv3, _, addr3 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + acc3 := input.ak.NewAccountWithAddress(ctx, addr3) + acc3.SetCoins(types.NewTestCoins()) + require.NoError(t, acc3.SetAccountNumber(2)) + input.ak.SetAccount(ctx, acc3) + + // msg and signatures + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + fee := types.NewTestStdFee() + + msgs := []sdk.Msg{msg} + + // test good tx from one signer + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // test sending it again fails (replay protection) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // fix sequence, should pass + seqs = []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx with another signer and correct sequences + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr3, addr1) + msgs = []sdk.Msg{msg1, msg2} + + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{2, 0, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // replay fails + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // tx from just second signer with incorrect sequence fails + msg = types.NewTestMsg(addr2) + msgs = []sdk.Msg{msg} + privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // fix the sequence and it passes + tx = types.NewTestTx(ctx, msgs, []crypto.PrivKey{priv2}, []uint64{1}, []uint64{1}, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // another tx from both of them that passes + msg = types.NewTestMsg(addr1, addr2) + msgs = []sdk.Msg{msg} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{3, 2} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) +} + +// Test logic around fee deduction. +func TestAnteHandlerFees(t *testing.T) { + // setup + input := setupTestInput() + ctx := input.ctx + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + _, _, addr2 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + input.ak.SetAccount(ctx, acc1) + + // msg and signatures + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + fee := types.NewTestStdFee() + msgs := []sdk.Msg{msg, bank.MsgSend{ + FromAddress: addr1, + ToAddress: addr2, + Amount: sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1000000)), + }} + + // signer does not have enough funds to pay the fee + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInsufficientFee) + + fee.Amount = sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1000)) + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 999))) + input.ak.SetAccount(ctx, acc1) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInsufficientFunds) + + require.True(t, input.sk.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().Empty()) + require.True(sdk.IntEq(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf(core.MicroSDRDenom), sdk.NewInt(999))) + + acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1000))) + input.ak.SetAccount(ctx, acc1) + checkValidTx(t, anteHandler, ctx, tx, false) + + require.True(sdk.IntEq(t, input.sk.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().AmountOf(core.MicroSDRDenom), sdk.NewInt(1000))) + require.True(sdk.IntEq(t, input.ak.GetAccount(ctx, addr1).GetCoins().AmountOf(core.MicroSDRDenom), sdk.NewInt(0))) +} + +// Test logic around memo gas consumption. +func TestAnteHandlerMemoGas(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + + // msg and signatures + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + fee := NewStdFee(0, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) + + // tx does not have enough gas + tx = types.NewTestTx(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeOutOfGas) + + // tx with memo doesn't have enough gas + fee = NewStdFee(801, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) + tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsd") + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeOutOfGas) + + // memo too large + fee = NewStdFee(9000, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) + tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, strings.Repeat("01234567890", 500)) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeMemoTooLarge) + + // tx with memo has enough gas + fee = NewStdFee(9000, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) + tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, strings.Repeat("0123456789", 10)) + checkValidTx(t, anteHandler, ctx, tx, false) +} + +func TestAnteHandlerMultiSigner(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + priv3, _, addr3 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + acc3 := input.ak.NewAccountWithAddress(ctx, addr3) + acc3.SetCoins(types.NewTestCoins()) + require.NoError(t, acc3.SetAccountNumber(2)) + input.ak.SetAccount(ctx, acc3) + + // set up msgs and fee + var tx sdk.Tx + msg1 := types.NewTestMsg(addr1, addr2) + msg2 := types.NewTestMsg(addr3, addr1) + msg3 := types.NewTestMsg(addr2, addr3) + msgs := []sdk.Msg{msg1, msg2, msg3} + fee := types.NewTestStdFee() + + // signers in order + privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + tx = types.NewTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "Check signers are in expected order and different account numbers works") + + checkValidTx(t, anteHandler, ctx, tx, false) + + // change sequence numbers + tx = types.NewTestTx(ctx, []sdk.Msg{msg1}, []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{1, 1}, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + tx = types.NewTestTx(ctx, []sdk.Msg{msg2}, []crypto.PrivKey{priv3, priv1}, []uint64{2, 0}, []uint64{1, 2}, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // expected seqs = [3, 2, 2] + tx = types.NewTestTxWithMemo(ctx, msgs, privs, accnums, []uint64{3, 2, 2}, fee, "Check signers are in expected order and different account numbers and sequence numbers works") + checkValidTx(t, anteHandler, ctx, tx, false) +} + +func TestAnteHandlerBadSignBytes(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + msgs := []sdk.Msg{msg} + fee := types.NewTestStdFee() + fee2 := types.NewTestStdFee() + fee2.Gas += 100 + fee3 := types.NewTestStdFee() + fee3.Amount[0].Amount = fee3.Amount[0].Amount.AddRaw(100) + + // test good tx and signBytes + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + chainID := ctx.ChainID() + chainID2 := chainID + "somemorestuff" + codeUnauth := sdk.CodeUnauthorized + + cases := []struct { + chainID string + accnum uint64 + seq uint64 + fee StdFee + msgs []sdk.Msg + code sdk.CodeType + }{ + {chainID2, 0, 1, fee, msgs, codeUnauth}, // test wrong chain_id + {chainID, 0, 2, fee, msgs, codeUnauth}, // test wrong seqs + {chainID, 1, 1, fee, msgs, codeUnauth}, // test wrong accnum + {chainID, 0, 1, fee, []sdk.Msg{types.NewTestMsg(addr2)}, codeUnauth}, // test wrong msg + {chainID, 0, 1, fee2, msgs, codeUnauth}, // test wrong fee + {chainID, 0, 1, fee3, msgs, codeUnauth}, // test wrong fee + } + + privs, seqs = []crypto.PrivKey{priv1}, []uint64{1} + for _, cs := range cases { + tx := types.NewTestTxWithSignBytes( + msgs, privs, accnums, seqs, fee, + StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, ""), + "", + ) + checkInvalidTx(t, anteHandler, ctx, tx, false, cs.code) + } + + // test wrong signer if public key exist + privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{0}, []uint64{1} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) + + // test wrong signer if public doesn't exist + msg = types.NewTestMsg(addr2) + msgs = []sdk.Msg{msg} + privs, accnums, seqs = []crypto.PrivKey{priv1}, []uint64{1}, []uint64{0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) +} + +func TestAnteHandlerSetPubKey(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + _, _, addr2 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + require.NoError(t, acc1.SetAccountNumber(0)) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + + var tx sdk.Tx + + // test good tx and set public key + msg := types.NewTestMsg(addr1) + msgs := []sdk.Msg{msg} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + fee := types.NewTestStdFee() + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + acc1 = input.ak.GetAccount(ctx, addr1) + require.Equal(t, acc1.GetPubKey(), priv1.PubKey()) + + // test public key not found + msg = types.NewTestMsg(addr2) + msgs = []sdk.Msg{msg} + tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) + sigs := tx.(StdTx).GetSignatures() + sigs[0].PubKey = nil + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) + + acc2 = input.ak.GetAccount(ctx, addr2) + require.Nil(t, acc2.GetPubKey()) + + // test invalid signature and public key + tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) + + acc2 = input.ak.GetAccount(ctx, addr2) + require.Nil(t, acc2.GetPubKey()) +} + +func TestProcessPubKey(t *testing.T) { + input := setupTestInput() + ctx := input.ctx + + // keys + _, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + + acc2.SetPubKey(priv2.PubKey()) + + type args struct { + acc Account + sig StdSignature + simulate bool + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"no sigs, simulate off", args{acc1, StdSignature{}, false}, true}, + {"no sigs, simulate on", args{acc1, StdSignature{}, true}, false}, + {"no sigs, account with pub, simulate on", args{acc2, StdSignature{}, true}, false}, + {"pubkey doesn't match addr, simulate off", args{acc1, StdSignature{PubKey: priv2.PubKey()}, false}, true}, + {"pubkey doesn't match addr, simulate on", args{acc1, StdSignature{PubKey: priv2.PubKey()}, true}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := ProcessPubKey(tt.args.acc, tt.args.sig, tt.args.simulate) + require.Equal(t, tt.wantErr, !err.IsOK()) + }) + } +} + +func TestConsumeSignatureVerificationGas(t *testing.T) { + params := DefaultParams() + msg := []byte{1, 2, 3, 4} + + pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false) + multisigKey1 := multisig.NewPubKeyMultisigThreshold(2, pkSet1) + multisignature1 := multisig.NewMultisig(len(pkSet1)) + expectedCost1 := expectedGasCostByKeys(pkSet1) + for i := 0; i < len(pkSet1); i++ { + multisignature1.AddSignatureFromPubKey(sigSet1[i], pkSet1[i], pkSet1) + } + + type args struct { + meter sdk.GasMeter + sig []byte + pubkey crypto.PubKey + params Params + } + tests := []struct { + name string + args args + gasConsumed uint64 + shouldErr bool + }{ + {"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, DefaultSigVerifyCostED25519, true}, + {"PubKeySecp256k1", args{sdk.NewInfiniteGasMeter(), nil, secp256k1.GenPrivKey().PubKey(), params}, DefaultSigVerifyCostSecp256k1, false}, + {"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1.Marshal(), multisigKey1, params}, expectedCost1, false}, + {"unknown key", args{sdk.NewInfiniteGasMeter(), nil, nil, params}, 0, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := DefaultSigVerificationGasConsumer(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) + + if tt.shouldErr { + require.False(t, res.IsOK()) + } else { + require.True(t, res.IsOK()) + require.Equal(t, tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed())) + } + }) + } +} + +func generatePubKeysAndSignatures(n int, msg []byte, keyTypeed25519 bool) (pubkeys []crypto.PubKey, signatures [][]byte) { + pubkeys = make([]crypto.PubKey, n) + signatures = make([][]byte, n) + for i := 0; i < n; i++ { + var privkey crypto.PrivKey + if rand.Int63()%2 == 0 { + privkey = ed25519.GenPrivKey() + } else { + privkey = secp256k1.GenPrivKey() + } + pubkeys[i] = privkey.PubKey() + signatures[i], _ = privkey.Sign(msg) + } + return +} + +func expectedGasCostByKeys(pubkeys []crypto.PubKey) uint64 { + cost := uint64(0) + for _, pubkey := range pubkeys { + pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey)) + switch { + case strings.Contains(pubkeyType, "ed25519"): + cost += DefaultParams().SigVerifyCostED25519 + case strings.Contains(pubkeyType, "secp256k1"): + cost += DefaultParams().SigVerifyCostSecp256k1 + default: + panic("unexpected key type") + } + } + return cost +} + +func TestCountSubkeys(t *testing.T) { + genPubKeys := func(n int) []crypto.PubKey { + var ret []crypto.PubKey + for i := 0; i < n; i++ { + ret = append(ret, secp256k1.GenPrivKey().PubKey()) + } + return ret + } + singleKey := secp256k1.GenPrivKey().PubKey() + singleLevelMultiKey := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) + multiLevelSubKey1 := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) + multiLevelSubKey2 := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) + multiLevelMultiKey := multisig.NewPubKeyMultisigThreshold(2, []crypto.PubKey{ + multiLevelSubKey1, multiLevelSubKey2, secp256k1.GenPrivKey().PubKey()}) + type args struct { + pub crypto.PubKey + } + tests := []struct { + name string + args args + want int + }{ + {"single key", args{singleKey}, 1}, + {"single level multikey", args{singleLevelMultiKey}, 5}, + {"multi level multikey", args{multiLevelMultiKey}, 11}, + } + for _, tt := range tests { + t.Run(tt.name, func(T *testing.T) { + require.Equal(t, tt.want, CountSubKeys(tt.args.pub)) + }) + } +} + +func TestAnteHandlerSigLimitExceeded(t *testing.T) { + // setup + input := setupTestInput() + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, DefaultSigVerificationGasConsumer) + ctx := input.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := types.KeyTestPubAddr() + priv2, _, addr2 := types.KeyTestPubAddr() + priv3, _, addr3 := types.KeyTestPubAddr() + priv4, _, addr4 := types.KeyTestPubAddr() + priv5, _, addr5 := types.KeyTestPubAddr() + priv6, _, addr6 := types.KeyTestPubAddr() + priv7, _, addr7 := types.KeyTestPubAddr() + priv8, _, addr8 := types.KeyTestPubAddr() + + // set the accounts + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(types.NewTestCoins()) + input.ak.SetAccount(ctx, acc1) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(types.NewTestCoins()) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + + var tx sdk.Tx + msg := types.NewTestMsg(addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8) + msgs := []sdk.Msg{msg} + fee := types.NewTestStdFee() + + // test rejection logic + privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8}, + []uint64{0, 0, 0, 0, 0, 0, 0, 0}, []uint64{0, 0, 0, 0, 0, 0, 0, 0} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeTooManySignatures) +} + +func TestEnsureSufficientMempoolFees(t *testing.T) { + // setup + input := setupTestInput() + ctx := input.ctx.WithMinGasPrices( + sdk.DecCoins{ + sdk.NewDecCoinFromDec("photino", sdk.NewDecWithPrec(50000000000000, sdk.Precision)), // 0.0001photino + sdk.NewDecCoinFromDec("uluna", sdk.NewDecWithPrec(10000000000000, sdk.Precision)), // 0.000001uluna + }, + ) + + testCases := []struct { + input StdFee + expectedOK bool + }{ + {NewStdFee(200000, sdk.NewCoins(sdk.NewInt64Coin("photino", 5))), false}, + {NewStdFee(200000, sdk.NewCoins(sdk.NewInt64Coin("uluna", 1))), false}, + {NewStdFee(200000, sdk.NewCoins(sdk.NewInt64Coin("uluna", 2))), true}, + {NewStdFee(200000, sdk.NewCoins(sdk.NewInt64Coin("photino", 10))), true}, + { + NewStdFee( + 200000, + sdk.NewCoins( + sdk.NewInt64Coin("photino", 10), + sdk.NewInt64Coin("uluna", 2), + ), + ), + true, + }, + { + NewStdFee( + 200000, + sdk.NewCoins( + sdk.NewInt64Coin(core.MicroSDRDenom, 5), + sdk.NewInt64Coin("photino", 10), + sdk.NewInt64Coin("uluna", 2), + ), + ), + true, + }, + } + + for i, tc := range testCases { + res := EnsureSufficientMempoolFees(ctx, tc.input, sdk.Coins{}) + require.Equal( + t, tc.expectedOK, res.IsOK(), + "unexpected result; tc #%d, input: %v, log: %v", i, tc.input, res.Log, + ) + } +} + +// Test custom SignatureVerificationGasConsumer +func TestCustomSignatureVerificationGasConsumer(t *testing.T) { + // setup + input := setupTestInput() + // setup an ante handler that only accepts PubKeyEd25519 + anteHandler := NewAnteHandler(input.ak, input.sk, input.tk, func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result { + switch pubkey := pubkey.(type) { + case ed25519.PubKeyEd25519: + meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") + return sdk.Result{} + default: + return sdk.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %T", pubkey)).Result() + } + }) + ctx := input.ctx.WithBlockHeight(1) + + // verify that an secp256k1 account gets rejected + priv1, _, addr1 := types.KeyTestPubAddr() + acc1 := input.ak.NewAccountWithAddress(ctx, addr1) + _ = acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))) + input.ak.SetAccount(ctx, acc1) + + var tx sdk.Tx + msg := types.NewTestMsg(addr1) + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + fee := types.NewTestStdFee() + msgs := []sdk.Msg{msg} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) + + // verify that an ed25519 account gets accepted + priv2 := ed25519.GenPrivKey() + pub2 := priv2.PubKey() + addr2 := sdk.AccAddress(pub2.Address()) + acc2 := input.ak.NewAccountWithAddress(ctx, addr2) + require.NoError(t, acc2.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150)))) + require.NoError(t, acc2.SetAccountNumber(1)) + input.ak.SetAccount(ctx, acc2) + msg = types.NewTestMsg(addr2) + privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0} + fee = types.NewTestStdFee() + msgs = []sdk.Msg{msg} + tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) +} diff --git a/x/auth/client/cli/estimate_fee.go b/x/auth/client/cli/estimate_fee.go new file mode 100644 index 000000000..7cb41ea98 --- /dev/null +++ b/x/auth/client/cli/estimate_fee.go @@ -0,0 +1,64 @@ +package cli + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + tutils "github.com/terra-project/core/x/auth/client/utils" +) + +// GetTxFeesEstimateCommand will create a send tx and sign it with the given key. +func GetTxFeesEstimateCommand(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "estimate-fee [file]", + Args: cobra.ExactArgs(1), + Short: "Estimate the required fee (stability + gas) and gas amount", + Long: strings.TrimSpace(` +Estimate fees for the given stdTx + +$ terracli tx estimate-fee [file] --gas-adjustment 1.4 --gas-prices 0.015uluna +`), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0]) + if err != nil { + return err + } + + gasAdjustment := viper.GetFloat64(client.FlagGasAdjustment) + + var gasPrices sdk.DecCoins + gasPricesStr := viper.GetString(client.FlagGasPrices) + if len(gasPricesStr) != 0 { + gasPrices, err = sdk.ParseDecCoins(gasPricesStr) + if err != nil { + return err + } + } + + fees, gas, err := tutils.ComputeFeesWithStdTx(cliCtx, stdTx, gasAdjustment, gasPrices) + + if err != nil { + return err + } + + response := tutils.EstimateFeeResp{Fees: fees, Gas: gas} + return cliCtx.PrintOutput(response) + }, + } + + cmd = client.GetCommands(cmd)[0] + + cmd.Flags().Float64(client.FlagGasAdjustment, client.DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ") + cmd.Flags().String(client.FlagGasPrices, "", "Gas prices to determine the transaction fee (e.g. 10uluna)") + + return cmd +} diff --git a/x/auth/client/rest/estimate_fee.go b/x/auth/client/rest/estimate_fee.go new file mode 100644 index 000000000..ffb050a39 --- /dev/null +++ b/x/auth/client/rest/estimate_fee.go @@ -0,0 +1,47 @@ +package rest + +import ( + "io/ioutil" + "net/http" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/types/rest" + + "github.com/terra-project/core/x/auth/client/utils" +) + +// EstimateTxFeeRequestHandlerFn returns estimated tx fee. In particular, +// it takes 'auto' for the gas field, then simulates and computes gas consumption. +func EstimateTxFeeRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req utils.EstimateFeeReq + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + err = cliCtx.Codec.UnmarshalJSON(body, &req) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + gasAdjustment, err := utils.ParseFloat64(req.GasAdjustment, client.DefaultGasAdjustment) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + fees, gas, err := utils.ComputeFeesWithStdTx(cliCtx, req.Tx, gasAdjustment, req.GasPrices) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + response := utils.EstimateFeeResp{Fees: fees, Gas: gas} + rest.PostProcessResponse(w, cliCtx, response) + } +} diff --git a/x/auth/client/rest/multisigntx.go b/x/auth/client/rest/multisigntx.go index 4b421eb32..7127c1941 100644 --- a/x/auth/client/rest/multisigntx.go +++ b/x/auth/client/rest/multisigntx.go @@ -1,4 +1,4 @@ -package cli +package rest import ( "fmt" @@ -6,22 +6,16 @@ import ( "net/http" "github.com/gorilla/mux" - "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/multisig" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - r.HandleFunc("/auth/accounts/{address}/multisign", MultiSignRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") -} - // MultiSignReq defines the properties of a multisign request's body. type MultiSignReq struct { Tx auth.StdTx `json:"tx"` @@ -29,15 +23,22 @@ type MultiSignReq struct { Signatures []auth.StdSignature `json:"signatures"` SignatureOnly bool `json:"signature_only"` Sequence uint64 `json:"sequence_number"` - Pubkey string `json:"pubkey"` // (optional) In case the multisig account never reveals its pubkey, it is required. + Pubkey MultiSignPubKey `json:"pubkey"` // (optional) In case the multisig account never reveals its pubkey, it is required. +} + +// MultiSignPubkey defines the properties of a multisig account's public key +type MultiSignPubKey struct { + Threshold int `json:"threshold"` + PubKeys []string `json:"pubkeys"` } // MultiSignRequestHandlerFn - http request handler to build multisign transaction. -func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +func MultiSignRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Decode params vars := mux.Vars(r) bech32Addr := vars["address"] + accGetter := types.NewAccountRetriever(cliCtx) multiSignAddr, err := sdk.AccAddressFromBech32(bech32Addr) if err != nil { @@ -45,7 +46,12 @@ func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context return } - multiSignAccount, err := cliCtx.GetAccount(multiSignAddr.Bytes()) + if err := accGetter.EnsureExists(multiSignAddr); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + multiSignAccount, err := accGetter.GetAccount(multiSignAddr) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -59,7 +65,7 @@ func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context return } - err = cdc.UnmarshalJSON(body, &req) + err = cliCtx.Codec.UnmarshalJSON(body, &req) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -69,13 +75,26 @@ func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context if multiSignAccount.GetPubKey() != nil { multisigPub = multiSignAccount.GetPubKey().(multisig.PubKeyMultisigThreshold) } else { - pubKey, err := sdk.GetAccPubKeyBech32(req.Pubkey) - if err != nil { + + var pubkeys []crypto.PubKey + for _, bechPubkey := range req.Pubkey.PubKeys { + pubkey, err := sdk.GetAccPubKeyBech32(bechPubkey) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + pubkeys = append(pubkeys, pubkey) + } + + // Ensure threshold <= len(pubkeys) + if req.Pubkey.Threshold > len(pubkeys) { + err := fmt.Errorf("Not sufficient pubkeys; required: %d given: %d", req.Pubkey.Threshold, len(pubkeys)) rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - multisigPub = pubKey.(multisig.PubKeyMultisigThreshold) + multisigPub = multisig.NewPubKeyMultisigThreshold(req.Pubkey.Threshold, pubkeys).(multisig.PubKeyMultisigThreshold) } multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys)) @@ -111,20 +130,20 @@ func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context } } - newStdSig := auth.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub} + newStdSig := auth.StdSignature{Signature: cliCtx.Codec.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub} newTx := auth.NewStdTx(req.Tx.GetMsgs(), req.Tx.Fee, []auth.StdSignature{newStdSig}, req.Tx.GetMemo()) sigOnly := req.SignatureOnly var json []byte switch { case sigOnly: - json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") + json, err = cliCtx.Codec.MarshalJSONIndent(newTx.Signatures[0], "", " ") case sigOnly && !cliCtx.Indent: - json, err = cdc.MarshalJSON(newTx.Signatures[0]) + json, err = cliCtx.Codec.MarshalJSON(newTx.Signatures[0]) case !sigOnly && cliCtx.Indent: - json, err = cdc.MarshalJSONIndent(newTx, "", " ") + json, err = cliCtx.Codec.MarshalJSONIndent(newTx, "", " ") default: - json, err = cdc.MarshalJSON(newTx) + json, err = cliCtx.Codec.MarshalJSON(newTx) } if err != nil { @@ -132,18 +151,7 @@ func MultiSignRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context return } - rest.PostProcessResponse(w, cdc, json, cliCtx.Indent) - return - } -} - -func readAndUnmarshalStdSignature(cdc *amino.Codec, filename string) (stdSig auth.StdSignature, err error) { - var bytes []byte - if bytes, err = ioutil.ReadFile(filename); err != nil { - return - } - if err = cdc.UnmarshalJSON(bytes, &stdSig); err != nil { + rest.PostProcessResponseBare(w, cliCtx, json) return } - return } diff --git a/x/auth/client/rest/rest.go b/x/auth/client/rest/rest.go new file mode 100644 index 000000000..0ace47a16 --- /dev/null +++ b/x/auth/client/rest/rest.go @@ -0,0 +1,17 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" +) + +// RegisterRoutes registers the auth module REST routes +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/auth/accounts/{address}/multisign", MultiSignRequestHandlerFn(cliCtx)).Methods("POST") +} + +// RegisterTxRoutes registers custom transaction routes on the provided router. +func RegisterTxRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/txs/estimate_fee", EstimateTxFeeRequestHandlerFn(cliCtx)).Methods("POST") +} diff --git a/x/auth/client/utils/feeutils.go b/x/auth/client/utils/feeutils.go new file mode 100644 index 000000000..903a40c0a --- /dev/null +++ b/x/auth/client/utils/feeutils.go @@ -0,0 +1,276 @@ +package utils + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + core "github.com/terra-project/core/types" + + "github.com/terra-project/core/x/treasury" +) + +type ( + // EstimateReq defines a tx encoding request. + EstimateFeeReq struct { + Tx auth.StdTx `json:"tx"` + GasAdjustment string `json:"gas_adjustment"` + GasPrices sdk.DecCoins `json:"gas_prices"` + } + + // EstimateResp defines a tx encoding response. + EstimateFeeResp struct { + Fees sdk.Coins `json:"fees"` + Gas uint64 `json:"gas"` + } +) + +func (r EstimateFeeResp) String() string { + return fmt.Sprintf(`EstimateFeeResp + fees: %s, + gas: %d`, + r.Fees, r.Gas) +} + +// ComputeFeesWithStdTx returns fee amount with given stdTx. +func ComputeFeesWithStdTx( + cliCtx context.CLIContext, + tx auth.StdTx, + gasAdjustment float64, + gasPrices sdk.DecCoins) (fees sdk.Coins, gas uint64, err error) { + + gas = tx.Fee.Gas + sim := (gas == 0) + + if sim { + tx.Signatures = []auth.StdSignature{{}} + txBytes, err := utils.GetTxEncoder(cliCtx.Codec)(tx) + if err != nil { + return nil, 0, err + } + + _, adj, err := utils.CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, gasAdjustment) + + if err != nil { + return nil, 0, err + } + + gas = adj + } + + // Computes taxes of the msgs + taxes, err := filterMsgAndComputeTax(cliCtx, tx.Msgs) + if err != nil { + return nil, 0, err + } + + fees = fees.Add(taxes) + + if !gasPrices.IsZero() { + glDec := sdk.NewDec(int64(gas)) + + // Derive the fees based on the provided gas prices, where + // fee = ceil(gasPrice * gasLimit). + gasFees := make(sdk.Coins, len(gasPrices)) + for i, gp := range gasPrices { + fee := gp.Amount.Mul(glDec) + gasFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + fees = fees.Add(gasFees.Sort()) + } + + return +} + +// ComputeReqParams no-lint +type ComputeReqParams struct { + Memo string + ChainID string + AccountNumber uint64 + Sequence uint64 + GasPrices sdk.DecCoins + Gas string + GasAdjustment string + + Msgs []sdk.Msg +} + +// ComputeFee returns fee amount with given transfer, gas, gas prices, and fees amount. +func ComputeFees( + cliCtx context.CLIContext, + req ComputeReqParams) (fees sdk.Coins, gas uint64, err error) { + + gasPrices := req.GasPrices + gasAdj, err := ParseFloat64(req.GasAdjustment, client.DefaultGasAdjustment) + if err != nil { + return nil, 0, err + } + + if req.Gas == "0" { + req.Gas = client.GasFlagAuto + } + + sim, gas, err := client.ParseGas(req.Gas) + txBldr := auth.NewTxBuilder( + utils.GetTxEncoder(cliCtx.Codec), req.AccountNumber, req.Sequence, client.DefaultGasLimit, gasAdj, + sim, req.ChainID, req.Memo, sdk.Coins{}, req.GasPrices, + ) + + kb, _ := keys.NewKeyBaseFromHomeFlag() + txBldr = txBldr.WithKeybase(kb) + + if sim { + txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, req.Msgs) + if err != nil { + return nil, 0, err + } + + gas = txBldr.Gas() + } + + // Computes taxes of the msgs + taxes, err := filterMsgAndComputeTax(cliCtx, req.Msgs) + if err != nil { + return nil, 0, err + } + + fees = fees.Add(taxes) + + if !gasPrices.IsZero() { + glDec := sdk.NewDec(int64(gas)) + + // Derive the fees based on the provided gas prices, where + // fee = ceil(gasPrice * gasLimit). + gasFees := make(sdk.Coins, len(gasPrices)) + for i, gp := range gasPrices { + fee := gp.Amount.Mul(glDec) + gasFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + fees = fees.Add(gasFees.Sort()) + } + + return +} + +// filterMsgAndComputeTax computes the stability tax on MsgSend and MsgMultiSend. +func filterMsgAndComputeTax(cliCtx context.CLIContext, msgs []sdk.Msg) (taxes sdk.Coins, err error) { + taxRate, err := queryTaxRate(cliCtx) + if err != nil { + return nil, err + } + + for _, msg := range msgs { + switch msg := msg.(type) { + case bank.MsgSend: + tax, err := computeTax(cliCtx, taxRate, msg.Amount) + if err != nil { + return nil, err + } + + taxes = taxes.Add(tax) + + case bank.MsgMultiSend: + for _, input := range msg.Inputs { + tax, err := computeTax(cliCtx, taxRate, input.Coins) + if err != nil { + return nil, err + } + + taxes = taxes.Add(tax) + } + } + } + + return +} + +// computes the stability tax according to tax-rate and tax-cap +func computeTax(cliCtx context.CLIContext, taxRate sdk.Dec, principal sdk.Coins) (taxes sdk.Coins, err error) { + + for _, coin := range principal { + + if coin.Denom == core.MicroLunaDenom { + continue + } + + taxCap, err := queryTaxCap(cliCtx, coin.Denom) + if err != nil { + return nil, err + } + + taxDue := sdk.NewDecFromInt(coin.Amount).Mul(taxRate).TruncateInt() + + // If tax due is greater than the tax cap, cap! + if taxDue.GT(taxCap) { + taxDue = taxCap + } + + if taxDue.Equal(sdk.ZeroInt()) { + continue + } + + taxes = taxes.Add(sdk.NewCoins(sdk.NewCoin(coin.Denom, taxDue))) + } + + return +} + +func queryTaxRate(cliCtx context.CLIContext) (sdk.Dec, error) { + // Query current-epoch + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + if err != nil { + return sdk.Dec{}, err + } + + var epoch int64 + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) + + params := treasury.NewQueryTaxRateParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + // Query tax-rate + res, _, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryTaxRate), bz) + if err != nil { + return sdk.Dec{}, err + } + + var taxRate sdk.Dec + cliCtx.Codec.MustUnmarshalJSON(res, &taxRate) + return taxRate, nil +} + +func queryTaxCap(cliCtx context.CLIContext, denom string) (sdk.Int, error) { + // Query tax-cap + + params := treasury.NewQueryTaxCapParams(denom) + bz := cliCtx.Codec.MustMarshalJSON(params) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryTaxCap), bz) + if err != nil { + return sdk.Int{}, err + } + + var taxCap sdk.Int + cliCtx.Codec.MustUnmarshalJSON(res, &taxCap) + + return taxCap, nil +} + +// parse string to float64 +func ParseFloat64(s string, defaultIfEmpty float64) (n float64, err error) { + if len(s) == 0 { + return defaultIfEmpty, nil + } + + n, err = strconv.ParseFloat(s, 64) + + return +} diff --git a/x/auth/cosmos_alias.go b/x/auth/cosmos_alias.go new file mode 100644 index 000000000..9fa84e293 --- /dev/null +++ b/x/auth/cosmos_alias.go @@ -0,0 +1,82 @@ +// nolint +package auth + +import ( + "github.com/cosmos/cosmos-sdk/x/auth" +) + +const ( + ModuleName = auth.ModuleName + StoreKey = auth.StoreKey + FeeCollectorName = auth.FeeCollectorName + QuerierRoute = auth.QuerierRoute + DefaultParamspace = auth.DefaultParamspace + DefaultMaxMemoCharacters = auth.DefaultMaxMemoCharacters + DefaultTxSigLimit = auth.DefaultTxSigLimit + DefaultTxSizeCostPerByte = auth.DefaultTxSizeCostPerByte + DefaultSigVerifyCostED25519 = auth.DefaultSigVerifyCostED25519 + DefaultSigVerifyCostSecp256k1 = auth.DefaultSigVerifyCostSecp256k1 + QueryAccount = auth.QueryAccount +) + +var ( + // functions aliases + NewBaseAccount = auth.NewBaseAccount + ProtoBaseAccount = auth.ProtoBaseAccount + NewBaseAccountWithAddress = auth.NewBaseAccountWithAddress + NewBaseVestingAccount = auth.NewBaseVestingAccount + NewContinuousVestingAccountRaw = auth.NewContinuousVestingAccountRaw + NewContinuousVestingAccount = auth.NewContinuousVestingAccount + NewDelayedVestingAccountRaw = auth.NewDelayedVestingAccountRaw + NewDelayedVestingAccount = auth.NewDelayedVestingAccount + NewGenesisState = auth.NewGenesisState + ValidateGenesis = auth.ValidateGenesis + AddressStoreKey = auth.AddressStoreKey + NewParams = auth.NewParams + ParamKeyTable = auth.ParamKeyTable + DefaultParams = auth.DefaultParams + NewQueryAccountParams = auth.NewQueryAccountParams + NewStdTx = auth.NewStdTx + CountSubKeys = auth.CountSubKeys + NewStdFee = auth.NewStdFee + StdSignBytes = auth.StdSignBytes + DefaultTxDecoder = auth.DefaultTxDecoder + DefaultTxEncoder = auth.DefaultTxEncoder + NewTxBuilder = auth.NewTxBuilder + NewTxBuilderFromCLI = auth.NewTxBuilderFromCLI + MakeSignature = auth.MakeSignature + NewAccountRetriever = auth.NewAccountRetriever + NewAccountKeeper = auth.NewAccountKeeper + + // variable aliases + AddressStoreKeyPrefix = auth.AddressStoreKeyPrefix + GlobalAccountNumberKey = auth.GlobalAccountNumberKey + KeyMaxMemoCharacters = auth.KeyMaxMemoCharacters + KeyTxSigLimit = auth.KeyTxSigLimit + KeyTxSizeCostPerByte = auth.KeyTxSizeCostPerByte + KeySigVerifyCostED25519 = auth.KeySigVerifyCostED25519 + KeySigVerifyCostSecp256k1 = auth.KeySigVerifyCostSecp256k1 + CosmosModuleCdc = auth.ModuleCdc + NewCosmosAppModule = auth.NewAppModule +) + +type ( + Account = auth.Account + VestingAccount = auth.VestingAccount + BaseAccount = auth.BaseAccount + BaseVestingAccount = auth.BaseVestingAccount + ContinuousVestingAccount = auth.ContinuousVestingAccount + DelayedVestingAccount = auth.DelayedVestingAccount + GenesisState = auth.GenesisState + Params = auth.Params + QueryAccountParams = auth.QueryAccountParams + StdSignMsg = auth.StdSignMsg + StdTx = auth.StdTx + StdFee = auth.StdFee + StdSignDoc = auth.StdSignDoc + StdSignature = auth.StdSignature + TxBuilder = auth.TxBuilder + AccountKeeper = auth.AccountKeeper + CosmosAppModule = auth.AppModule + CosmosAppModuleBasic = auth.AppModuleBasic +) diff --git a/x/auth/internal/types/codec.go b/x/auth/internal/types/codec.go new file mode 100644 index 000000000..8ac3aada4 --- /dev/null +++ b/x/auth/internal/types/codec.go @@ -0,0 +1,30 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +// RegisterCodec registers concrete types on the codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*auth.Account)(nil), nil) + cdc.RegisterConcrete(&auth.BaseAccount{}, "core/Account", nil) + cdc.RegisterInterface((*auth.VestingAccount)(nil), nil) + cdc.RegisterConcrete(&auth.BaseVestingAccount{}, "core/BaseVestingAccount", nil) + cdc.RegisterConcrete(&auth.ContinuousVestingAccount{}, "core/ContinuousVestingAccount", nil) + cdc.RegisterConcrete(&auth.DelayedVestingAccount{}, "core/DelayedVestingAccount", nil) + cdc.RegisterConcrete(auth.StdTx{}, "core/StdTx", nil) + cdc.RegisterConcrete(&LazySchedule{}, "core/Schedule", nil) + cdc.RegisterConcrete(&VestingSchedule{}, "core/VestingSchedule", nil) + cdc.RegisterConcrete(&BaseLazyGradedVestingAccount{}, "core/LazyGradedVestingAccount", nil) +} + +// module wide codec +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/auth/internal/types/expected_keeper.go b/x/auth/internal/types/expected_keeper.go new file mode 100644 index 000000000..a87f0ee2b --- /dev/null +++ b/x/auth/internal/types/expected_keeper.go @@ -0,0 +1,20 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/supply" +) + +// TreasuryKeeper is expected keeper for treasury +type TreasuryKeeper interface { + GetTaxRate(ctx sdk.Context, epoch int64) (rate sdk.Dec) + GetTaxCap(ctx sdk.Context, denom string) (taxCap sdk.Int) + RecordTaxProceeds(ctx sdk.Context, delta sdk.Coins) +} + +// SupplyKeeper defines the expected supply Keeper (noalias) +type SupplyKeeper interface { + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + GetModuleAccount(ctx sdk.Context, moduleName string) supply.ModuleAccountI + GetModuleAddress(moduleName string) sdk.AccAddress +} diff --git a/types/lazy_graded_account.go b/x/auth/internal/types/lazy_vesting.go similarity index 82% rename from types/lazy_graded_account.go rename to x/auth/internal/types/lazy_vesting.go index 520d4a3cf..fcabf739b 100644 --- a/types/lazy_graded_account.go +++ b/x/auth/internal/types/lazy_vesting.go @@ -63,24 +63,24 @@ func (s LazySchedule) IsValid() bool { //----------------------------------------------------------------------------- // Vesting Lazy Schedule -// LazyVestingSchedule maps the ratio of tokens that becomes vested by blocktime (in seconds) from genesis. +// VestingSchedule maps the ratio of tokens that becomes vested by blocktime (in seconds) from genesis. // The sum of values in the LazySchedule should sum to 1.0. // CONTRACT: assumes that entries are -type LazyVestingSchedule struct { +type VestingSchedule struct { Denom string `json:"denom"` LazySchedules []LazySchedule `json:"schedules"` // maps blocktime to percentage vested. Should sum to 1. } -// NewLazyVestingSchedule creates a new vesting lazy schedule instance. -func NewLazyVestingSchedule(denom string, lazySchedules []LazySchedule) LazyVestingSchedule { - return LazyVestingSchedule{ +// NewVestingSchedule creates a new vesting lazy schedule instance. +func NewVestingSchedule(denom string, lazySchedules []LazySchedule) VestingSchedule { + return VestingSchedule{ Denom: denom, LazySchedules: lazySchedules, } } // GetVestedRatio returns the ratio of tokens that have vested by blockTime. -func (vs LazyVestingSchedule) GetVestedRatio(blockTime int64) sdk.Dec { +func (vs VestingSchedule) GetVestedRatio(blockTime int64) sdk.Dec { sumRatio := sdk.ZeroDec() for _, lazySchedule := range vs.LazySchedules { startTime := lazySchedule.GetStartTime() @@ -102,12 +102,12 @@ func (vs LazyVestingSchedule) GetVestedRatio(blockTime int64) sdk.Dec { } // GetDenom returns the denom of vesting layz schedule -func (vs LazyVestingSchedule) GetDenom() string { +func (vs VestingSchedule) GetDenom() string { return vs.Denom } // IsValid checks that the vesting lazy schedule is valid. -func (vs LazyVestingSchedule) IsValid() bool { +func (vs VestingSchedule) IsValid() bool { sumRatio := sdk.ZeroDec() for _, lazySchedule := range vs.LazySchedules { @@ -122,8 +122,8 @@ func (vs LazyVestingSchedule) IsValid() bool { } // String implements the fmt.Stringer interface -func (vs LazyVestingSchedule) String() string { - return fmt.Sprintf(`LazyVestingSchedule: +func (vs VestingSchedule) String() string { + return fmt.Sprintf(`VestingSchedule: Denom: %v, LazySchedules: %v`, vs.Denom, vs.LazySchedules) @@ -136,8 +136,8 @@ func (vs LazyVestingSchedule) String() string { type LazyGradedVestingAccount interface { auth.VestingAccount - GetLazyVestingSchedules() []LazyVestingSchedule - GetLazyVestingSchedule(denom string) (LazyVestingSchedule, bool) + GetVestingSchedules() []VestingSchedule + GetVestingSchedule(denom string) (VestingSchedule, bool) } // BaseLazyGradedVestingAccount implements the LazyGradedVestingAccount interface. It vests all @@ -149,11 +149,16 @@ var _ LazyGradedVestingAccount = (*BaseLazyGradedVestingAccount)(nil) type BaseLazyGradedVestingAccount struct { *auth.BaseVestingAccount - LazyVestingSchedules []LazyVestingSchedule `json:"vesting_lazy_schedules"` + VestingSchedules []VestingSchedule `json:"vesting_schedules"` } -// NewBaseLazyGradedVestingAccount returns a BaseLazyGradedVestingAccount -func NewBaseLazyGradedVestingAccount(baseAcc *auth.BaseAccount, lazyVestingSchedules []LazyVestingSchedule) *BaseLazyGradedVestingAccount { +// NewBaseLazyGradedVestingAccount creates a new BaseLazyGradedVestingAccount object from BaseVestingAccount +func NewBaseLazyGradedVestingAccountRaw(baseVestingAcc *auth.BaseVestingAccount, lazyVestingSchedules []VestingSchedule) *BaseLazyGradedVestingAccount { + return &BaseLazyGradedVestingAccount{baseVestingAcc, lazyVestingSchedules} +} + +// NewBaseLazyGradedVestingAccount returns a new BaseLazyGradedVestingAccount +func NewBaseLazyGradedVestingAccount(baseAcc *auth.BaseAccount, lazyVestingSchedules []VestingSchedule) *BaseLazyGradedVestingAccount { baseVestingAcc := &auth.BaseVestingAccount{ BaseAccount: baseAcc, OriginalVesting: baseAcc.Coins, @@ -163,20 +168,20 @@ func NewBaseLazyGradedVestingAccount(baseAcc *auth.BaseAccount, lazyVestingSched return &BaseLazyGradedVestingAccount{baseVestingAcc, lazyVestingSchedules} } -// GetLazyVestingSchedules returns the LazyVestingSchedules of the graded lazy vesting account -func (lgva BaseLazyGradedVestingAccount) GetLazyVestingSchedules() []LazyVestingSchedule { - return lgva.LazyVestingSchedules +// GetVestingSchedules returns the VestingSchedules of the graded lazy vesting account +func (lgva BaseLazyGradedVestingAccount) GetVestingSchedules() []VestingSchedule { + return lgva.VestingSchedules } -// GetLazyVestingSchedule returns the VestingSchedule of the given denom -func (lgva BaseLazyGradedVestingAccount) GetLazyVestingSchedule(denom string) (LazyVestingSchedule, bool) { - for _, vs := range lgva.LazyVestingSchedules { +// GetVestingSchedule returns the VestingSchedule of the given denom +func (lgva BaseLazyGradedVestingAccount) GetVestingSchedule(denom string) (VestingSchedule, bool) { + for _, vs := range lgva.VestingSchedules { if vs.Denom == denom { return vs, true } } - return LazyVestingSchedule{}, false + return VestingSchedule{}, false } // GetVestedCoins returns the total amount of vested coins for a graded vesting @@ -184,7 +189,7 @@ func (lgva BaseLazyGradedVestingAccount) GetLazyVestingSchedule(denom string) (L func (lgva BaseLazyGradedVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins { var vestedCoins sdk.Coins for _, ovc := range lgva.OriginalVesting { - if vestingSchedule, exists := lgva.GetLazyVestingSchedule(ovc.Denom); exists { + if vestingSchedule, exists := lgva.GetVestingSchedule(ovc.Denom); exists { vestedRatio := vestingSchedule.GetVestedRatio(blockTime.Unix()) vestedAmt := ovc.Amount.ToDec().Mul(vestedRatio).RoundInt() if vestedAmt.Equal(sdk.ZeroInt()) { @@ -244,10 +249,10 @@ func (lgva BaseLazyGradedVestingAccount) String() string { OriginalVesting: %s DelegatedFree: %s DelegatedVesting: %s - LazyVestingSchedules: %v `, + VestingSchedules: %v `, lgva.Address, pubkey, lgva.Coins, lgva.AccountNumber, lgva.Sequence, lgva.OriginalVesting, lgva.DelegatedFree, lgva.DelegatedVesting, - lgva.LazyVestingSchedules, + lgva.VestingSchedules, ) } diff --git a/x/auth/module.go b/x/auth/module.go new file mode 100644 index 000000000..a5f634463 --- /dev/null +++ b/x/auth/module.go @@ -0,0 +1,119 @@ +package auth + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint (DO NOT TOUCH) +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module object +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(accountKeeper AccountKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(accountKeeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/auth/simulation/fake.go b/x/auth/simulation/fake.go new file mode 100644 index 000000000..7c62435ab --- /dev/null +++ b/x/auth/simulation/fake.go @@ -0,0 +1,75 @@ +package simulation + +import ( + "errors" + "math/big" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// This simulation is forked because of the absence of the Mint Module +// SimulateDeductFee generates simulation cases that accounts send token to FeeCollector module account +func SimulateDeductFee(ak auth.AccountKeeper, supplyKeeper types.SupplyKeeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) ( + opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + account := simulation.RandomAcc(r, accs) + stored := ak.GetAccount(ctx, account.Address) + initCoins := stored.GetCoins() + opMsg = simulation.NewOperationMsgBasic(types.ModuleName, "deduct_fee", "", false, nil) + + // NOTE - terra does not have Mint module, so feeCollector account will not exist here + // feeCollector := ak.GetAccount(ctx, supplyKeeper.GetModuleAddress(types.FeeCollectorName)) + // if feeCollector == nil { + // panic(fmt.Errorf("fee collector account hasn't been set")) + // } + + if len(initCoins) == 0 { + return opMsg, nil, nil + } + + denomIndex := r.Intn(len(initCoins)) + randCoin := initCoins[denomIndex] + + amt, err := randPositiveInt(r, randCoin.Amount) + if err != nil { + return opMsg, nil, nil + } + + // Create a random fee and verify the fees are within the account's spendable + // balance. + fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt)) + spendableCoins := stored.SpendableCoins(ctx.BlockHeader().Time) + if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { + return opMsg, nil, nil + } + + // get the new account balance + _, hasNeg := initCoins.SafeSub(fees) + if hasNeg { + return opMsg, nil, nil + } + + err = supplyKeeper.SendCoinsFromAccountToModule(ctx, stored.GetAddress(), types.FeeCollectorName, fees) + if err != nil { + panic(err) + } + + opMsg.OK = true + return opMsg, nil, nil + } +} + +func randPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { + if !max.GT(sdk.OneInt()) { + return sdk.Int{}, errors.New("max too small") + } + max = max.Sub(sdk.OneInt()) + return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil +} diff --git a/x/auth/test_utils.go b/x/auth/test_utils.go new file mode 100644 index 000000000..28f0a47f8 --- /dev/null +++ b/x/auth/test_utils.go @@ -0,0 +1,175 @@ +package auth + +import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/params" + "github.com/terra-project/core/x/supply" +) + +type testInput struct { + cdc *codec.Codec + ctx sdk.Context + ak AccountKeeper + sk SupplyKeeper + tk TreasuryKeeper +} + +// moduleAccount defines an account for modules that holds coins on a pool +type moduleAccount struct { + *BaseAccount + name string // name of the module + permissions []string // permissions of module account +} + +// HasPermission returns whether or not the module account has permission. +func (ma moduleAccount) HasPermission(permission string) bool { + for _, perm := range ma.permissions { + if perm == permission { + return true + } + } + return false +} + +// GetName returns the the name of the holder's module +func (ma moduleAccount) GetName() string { + return ma.name +} + +// GetPermissions returns permissions granted to the module account +func (ma moduleAccount) GetPermissions() []string { + return ma.permissions +} + +func setupTestInput() testInput { + db := dbm.NewMemDB() + + cdc := codec.New() + RegisterCodec(cdc) + cdc.RegisterInterface((*supply.ModuleAccountI)(nil), nil) + cdc.RegisterConcrete(&moduleAccount{}, "core/ModuleAccount", nil) + codec.RegisterCrypto(cdc) + + authCapKey := sdk.NewKVStoreKey("authCapKey") + keyParams := sdk.NewKVStoreKey("subspace") + tkeyParams := sdk.NewTransientStoreKey("transient_subspace") + + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + err := ms.LoadLatestVersion() + if err != nil { + panic(err) + } + + ps := params.NewSubspace(cdc, keyParams, tkeyParams, DefaultParamspace) + ak := NewAccountKeeper(cdc, authCapKey, ps, ProtoBaseAccount) + sk := NewDummySupplyKeeper(ak) + + ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger()) + + ak.SetParams(ctx, DefaultParams()) + + tk := NewDummyTreasuryKeeper() + + return testInput{cdc: cdc, ctx: ctx, ak: ak, sk: sk, tk: tk} +} + +// DummyTreasuryKeeper no-lint +type DummyTreasuryKeeper struct{} + +func NewDummyTreasuryKeeper() DummyTreasuryKeeper { return DummyTreasuryKeeper{} } + +// GetTaxRate for the dummy treasury keeper +func (tk DummyTreasuryKeeper) GetTaxRate(_ sdk.Context, _ int64) (rate sdk.Dec) { + return sdk.NewDecWithPrec(1, 3) // 0.1% +} + +// GetTaxCap for the dummy treasury keeper +func (tk DummyTreasuryKeeper) GetTaxCap(_ sdk.Context, _ string) (taxCap sdk.Int) { + return sdk.OneInt() +} + +// RecordTaxProceeds for the dummy treasury keeper +func (tk DummyTreasuryKeeper) RecordTaxProceeds(_ sdk.Context, _ sdk.Coins) { + return +} + +// DummySupplyKeeper defines a supply keeper used only for testing to avoid +// circle dependencies +type DummySupplyKeeper struct { + ak AccountKeeper +} + +// NewDummySupplyKeeper creates a DummySupplyKeeper instance +func NewDummySupplyKeeper(ak AccountKeeper) DummySupplyKeeper { + return DummySupplyKeeper{ak} +} + +// SendCoinsFromAccountToModule for the dummy supply keeper +func (sk DummySupplyKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, fromAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error { + + fromAcc := sk.ak.GetAccount(ctx, fromAddr) + moduleAcc := sk.GetModuleAccount(ctx, recipientModule) + + newFromCoins, hasNeg := fromAcc.GetCoins().SafeSub(amt) + if hasNeg { + return sdk.ErrInsufficientCoins(fromAcc.GetCoins().String()) + } + + newToCoins := moduleAcc.GetCoins().Add(amt) + + if err := fromAcc.SetCoins(newFromCoins); err != nil { + return sdk.ErrInternal(err.Error()) + } + + if err := moduleAcc.SetCoins(newToCoins); err != nil { + return sdk.ErrInternal(err.Error()) + } + + sk.ak.SetAccount(ctx, fromAcc) + sk.ak.SetAccount(ctx, moduleAcc) + + return nil +} + +// GetModuleAccount for dummy supply keeper +func (sk DummySupplyKeeper) GetModuleAccount(ctx sdk.Context, moduleName string) supply.ModuleAccountI { + addr := sk.GetModuleAddress(moduleName) + + acc := sk.ak.GetAccount(ctx, addr) + if acc != nil { + macc, ok := acc.(supply.ModuleAccountI) + if ok { + return macc + } + } + + moduleAddress := sk.GetModuleAddress(moduleName) + baseAcc := NewBaseAccountWithAddress(moduleAddress) + + // create a new module account + macc := &moduleAccount{ + BaseAccount: &baseAcc, + name: moduleName, + permissions: []string{"basic"}, + } + + maccI := (sk.ak.NewAccount(ctx, macc)).(supply.ModuleAccountI) + sk.ak.SetAccount(ctx, maccI) + return maccI +} + +// GetModuleAddress for dummy supply keeper +func (sk DummySupplyKeeper) GetModuleAddress(moduleName string) sdk.AccAddress { + return sdk.AccAddress(crypto.AddressHash([]byte(moduleName))) +} diff --git a/x/bank/alias.go b/x/bank/alias.go new file mode 100644 index 000000000..ee5f46bcd --- /dev/null +++ b/x/bank/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/bank/internal/types/ +package bank + +import ( + "github.com/terra-project/core/x/bank/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go new file mode 100644 index 000000000..cd9fc7828 --- /dev/null +++ b/x/bank/client/cli/tx.go @@ -0,0 +1,87 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/bank" + + feeutils "github.com/terra-project/core/x/auth/client/utils" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + txCmd := &cobra.Command{ + Use: bank.ModuleName, + Short: "Bank transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + txCmd.AddCommand( + SendTxCmd(cdc), + ) + return txCmd +} + +// SendTxCmd will create a send tx and sign it with the given key. +func SendTxCmd(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "send [from_key_or_address] [to_address] [coins]", + Short: "Create and sign a send tx", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContextWithFrom(args[0]).WithCodec(cdc) + + to, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + // parse coins trying to be sent + coins, err := sdk.ParseCoins(args[2]) + if err != nil { + return err + } + + // build and sign the transaction, then broadcast to Tendermint + msg := bank.MsgSend{FromAddress: cliCtx.GetFromAddress(), ToAddress: to, Amount: coins} + + if !cliCtx.GenerateOnly && txBldr.Fees().IsZero() { + // extimate tax and gas + fees, gas, err := feeutils.ComputeFees(cliCtx, feeutils.ComputeReqParams{ + Memo: txBldr.Memo(), + ChainID: txBldr.ChainID(), + AccountNumber: txBldr.AccountNumber(), + Sequence: txBldr.Sequence(), + GasPrices: txBldr.GasPrices(), + Gas: fmt.Sprintf("%d", txBldr.Gas()), + GasAdjustment: fmt.Sprintf("%f", txBldr.GasAdjustment()), + Msgs: []sdk.Msg{msg}, + }) + + if err != nil { + return err + } + + // override gas and fees + txBldr = auth.NewTxBuilder(txBldr.TxEncoder(), txBldr.AccountNumber(), txBldr.Sequence(), + gas, txBldr.GasAdjustment(), false, txBldr.ChainID(), txBldr.Memo(), fees, sdk.DecCoins{}) + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + cmd = client.PostCommands(cmd)[0] + + return cmd +} diff --git a/x/bank/client/rest/tx.go b/x/bank/client/rest/tx.go new file mode 100644 index 000000000..901b98ea0 --- /dev/null +++ b/x/bank/client/rest/tx.go @@ -0,0 +1,87 @@ +package rest + +import ( + "net/http" + "strconv" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/cosmos/cosmos-sdk/x/bank" + bankrest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + + feeutils "github.com/terra-project/core/x/auth/client/utils" +) + +// RegisterRoutes - Central function to define routes that get registered by the main application +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cliCtx)).Methods("POST") + r.HandleFunc("/bank/balances/{address}", bankrest.QueryBalancesRequestHandlerFn(cliCtx)).Methods("GET") +} + +// SendReq defines the properties of a send request's body. +type SendReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + Coins sdk.Coins `json:"coins" yaml:"coins"` +} + +// SendRequestHandlerFn - http request handler to send coins to a address. +func SendRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bech32Addr := vars["address"] + + toAddr, err := sdk.AccAddressFromBech32(bech32Addr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + var req SendReq + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := bank.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: req.Coins} + + if req.BaseReq.Fees.IsZero() { + fees, gas, err := feeutils.ComputeFees(cliCtx, feeutils.ComputeReqParams{ + Memo: req.BaseReq.Memo, + ChainID: req.BaseReq.ChainID, + AccountNumber: req.BaseReq.AccountNumber, + Sequence: req.BaseReq.Sequence, + GasPrices: req.BaseReq.GasPrices, + Gas: req.BaseReq.Gas, + GasAdjustment: req.BaseReq.GasAdjustment, + Msgs: []sdk.Msg{msg}, + }) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // override gas and fees + req.BaseReq.Gas = strconv.FormatUint(gas, 10) + req.BaseReq.Fees = fees + req.BaseReq.GasPrices = sdk.DecCoins{} + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + } +} diff --git a/x/bank/cosmos_alias.go b/x/bank/cosmos_alias.go new file mode 100644 index 000000000..313555380 --- /dev/null +++ b/x/bank/cosmos_alias.go @@ -0,0 +1,46 @@ +// nolint +package bank + +import ( + "github.com/cosmos/cosmos-sdk/x/bank" +) + +const ( + DefaultCodespace = bank.DefaultCodespace + CodeSendDisabled = bank.CodeSendDisabled + CodeInvalidInputsOutputs = bank.CodeInvalidInputsOutputs + ModuleName = bank.ModuleName + RouterKey = bank.RouterKey + QuerierRoute = bank.QuerierRoute + DefaultParamspace = bank.DefaultParamspace +) + +var ( + // functions aliases + ErrNoInputs = bank.ErrNoInputs + ErrNoOutputs = bank.ErrNoOutputs + ErrInputOutputMismatch = bank.ErrInputOutputMismatch + ErrSendDisabled = bank.ErrSendDisabled + NewBaseKeeper = bank.NewBaseKeeper + NewInput = bank.NewInput + NewOutput = bank.NewOutput + ParamKeyTable = bank.ParamKeyTable + NewCosmosAppModule = bank.NewAppModule + SimulateMsgSend = bank.SimulateMsgSend + SimulateSingleInputMsgMultiSend = bank.SimulateSingleInputMsgMultiSend + + // variable aliases + ParamStoreKeySendEnabled = bank.ParamStoreKeySendEnabled + CosmosModuleCdc = bank.ModuleCdc +) + +type ( + BaseKeeper = bank.BaseKeeper // ibc module depends on this + Keeper = bank.Keeper + MsgSend = bank.MsgSend + MsgMultiSend = bank.MsgMultiSend + Input = bank.Input + Output = bank.Output + CosmosAppModule = bank.AppModule + CosmosAppModuleBasic = bank.AppModuleBasic +) diff --git a/x/bank/internal/types/codec.go b/x/bank/internal/types/codec.go new file mode 100644 index 000000000..67360b66a --- /dev/null +++ b/x/bank/internal/types/codec.go @@ -0,0 +1,21 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(bank.MsgSend{}, "bank/MsgSend", nil) + cdc.RegisterConcrete(bank.MsgMultiSend{}, "bank/MsgMultiSend", nil) +} + +// module codec +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/bank/internal/types/expected_keepers.go b/x/bank/internal/types/expected_keepers.go new file mode 100644 index 000000000..642eae563 --- /dev/null +++ b/x/bank/internal/types/expected_keepers.go @@ -0,0 +1,18 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" +) + +// AccountKeeper defines the account contract that must be fulfilled when +// creating a x/bank keeper. +type AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + GetAllAccounts(ctx sdk.Context) []authexported.Account + SetAccount(ctx sdk.Context, acc authexported.Account) + + IterateAccounts(ctx sdk.Context, process func(authexported.Account) bool) +} diff --git a/x/bank/module.go b/x/bank/module.go new file mode 100644 index 000000000..8c9d730ec --- /dev/null +++ b/x/bank/module.go @@ -0,0 +1,124 @@ +package bank + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/terra-project/core/x/bank/client/cli" + "github.com/terra-project/core/x/bank/client/rest" + "github.com/terra-project/core/x/bank/internal/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + rest.RegisterRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper, accountKeeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/bench/bench_test_common.go b/x/bench/bench_test_common.go deleted file mode 100644 index c2dec26c2..000000000 --- a/x/bench/bench_test_common.go +++ /dev/null @@ -1,211 +0,0 @@ -package bench - -import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/budget" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/pay" - "github.com/terra-project/core/x/treasury" - - "time" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -const numOfValidators = 100 - -var ( - pubKeys [numOfValidators]crypto.PubKey - addrs [numOfValidators]sdk.AccAddress - - valConsPubKeys [numOfValidators]crypto.PubKey - valConsAddrs [numOfValidators]sdk.ConsAddress - - uLunaAmt = sdk.NewInt(10000000000).MulRaw(assets.MicroUnit) - uSDRAmt = sdk.NewInt(10000000000).MulRaw(assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - cdc *codec.Codec - bankKeeper bank.Keeper - budgetKeeper budget.Keeper - oracleKeeper oracle.Keeper - marketKeeper market.Keeper - mintKeeper mint.Keeper - treasuryKeeper treasury.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - pay.RegisterCodec(cdc) - treasury.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -// createTestInput common test code for bench test -func createTestInput() testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyMarket := sdk.NewKVStoreKey(market.StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyFee := sdk.NewKVStoreKey(auth.FeeStoreKey) - keyBudget := sdk.NewKVStoreKey(budget.StoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyTreasury := sdk.NewKVStoreKey(treasury.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyBudget, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyTreasury, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFee, sdk.StoreTypeIAVL, db) - - if err := ms.LoadLatestVersion(); err != nil { - panic(err) - } - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingParams := staking.DefaultParams() - stakingParams.BondDenom = assets.MicroLunaDenom - stakingKeeper.SetParams(ctx, stakingParams) - - feeKeeper := auth.NewFeeCollectionKeeper( - cdc, keyFee, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, stakingKeeper, feeKeeper, distr.DefaultCodespace, - ) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - sh := staking.NewHandler(stakingKeeper) - for i := 0; i < 100; i++ { - pubKeys[i] = secp256k1.GenPrivKey().PubKey() - addrs[i] = sdk.AccAddress(pubKeys[i].Address()) - - valConsPubKeys[i] = ed25519.GenPrivKey().PubKey() - valConsAddrs[i] = sdk.ConsAddress(valConsPubKeys[i].Address()) - - err2 := mintKeeper.Mint(ctx, addrs[i], sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt.MulRaw(3))) - if err2 != nil { - panic(err2) - } - - // Add validators - commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(sdk.ValAddress(addrs[i]), valConsPubKeys[i], - sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt), staking.Description{}, commission, sdk.OneInt()) - res := sh(ctx, msg) - if !res.IsOK() { - panic(res.Log) - } - - distrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(addrs[i])) - staking.EndBlocker(ctx, stakingKeeper) - } - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := market.NewKeeper( - cdc, - keyMarket, - oracleKeeper, - mintKeeper, - paramsKeeper.Subspace(market.DefaultParamspace)) - - treasuryKeeper := treasury.NewKeeper( - cdc, - keyTreasury, - stakingKeeper.GetValidatorSet(), - mintKeeper, - marketKeeper, - paramsKeeper.Subspace(treasury.DefaultParamspace), - ) - - budgetKeeper := budget.NewKeeper( - cdc, keyBudget, marketKeeper, mintKeeper, treasuryKeeper, stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(budget.DefaultParamspace), - ) - - budget.InitGenesis(ctx, budgetKeeper, budget.DefaultGenesisState()) - oracle.InitGenesis(ctx, oracleKeeper, oracle.DefaultGenesisState()) - treasury.InitGenesis(ctx, treasuryKeeper, treasury.DefaultGenesisState()) - - return testInput{ctx, cdc, bankKeeper, budgetKeeper, oracleKeeper, marketKeeper, mintKeeper, treasuryKeeper} -} diff --git a/x/bench/budget_bench_test.go b/x/bench/budget_bench_test.go deleted file mode 100644 index 5a717b49c..000000000 --- a/x/bench/budget_bench_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package bench - -import ( - "fmt" - "math/rand" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/terra-project/core/x/budget" -) - -func BenchmarkSubmitAndVoteProgramsPerBlock(b *testing.B) { - const numOfPrograms = 5 - input := createTestInput() - - defaultBudgetParams := budget.DefaultParams() - defaultBudgetParams.VotePeriod = 1 - input.budgetKeeper.SetParams(input.ctx, defaultBudgetParams) - - h := budget.NewHandler(input.budgetKeeper) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ctx := input.ctx.WithBlockHeight(int64(i)) - - for p := 0; p < numOfPrograms; p++ { - // registers programs - msg := budget.NewMsgSubmitProgram(fmt.Sprintf("test-%d-%d", i, p), "description", addrs[p], addrs[p+1]) - res := h(ctx, msg) - - if !res.IsOK() { - panic(res.Log) - } - - for v := 0; v < numOfValidators; v++ { - // votes to registered program - voteMsg := budget.NewMsgVoteProgram(uint64(i*numOfPrograms+p+1), rand.Intn(2) == 0, addrs[v]) - res := h(ctx, voteMsg) - - if !res.IsOK() { - panic(res.Log) - } - } - } - - budget.EndBlocker(ctx, input.budgetKeeper) - } - -} - -func BenchmarkSubmitAndWithdrawProgramPerBlock(b *testing.B) { - input := createTestInput() - - h := budget.NewHandler(input.budgetKeeper) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ctx := input.ctx.WithBlockHeight(int64(i)) - - for v := 0; v < numOfValidators; v++ { - var msg sdk.Msg - - if i%2 == 0 { - msg = budget.NewMsgSubmitProgram(fmt.Sprintf("test-%d-%d", i, v), "description", addrs[v], addrs[v]) - } else { - msg = budget.NewMsgWithdrawProgram(uint64((i/2)*numOfValidators+v+1), addrs[v]) - } - - res := h(ctx, msg) - if !res.IsOK() { - panic(res.Log) - } - } - - budget.EndBlocker(ctx, input.budgetKeeper) - } -} diff --git a/x/bench/oracle_bench_test.go b/x/bench/oracle_bench_test.go deleted file mode 100644 index d45de1f91..000000000 --- a/x/bench/oracle_bench_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package bench - -import ( - "encoding/hex" - "testing" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/oracle" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func BenchmarkOracleFeedVotePerBlock(b *testing.B) { - input := createTestInput() - - defaultOracleParams := oracle.DefaultParams() - defaultOracleParams.VotePeriod = 1 - input.oracleKeeper.SetParams(input.ctx, defaultOracleParams) - - h := oracle.NewHandler(input.oracleKeeper) - - denoms := []string{ - assets.MicroSDRDenom, - assets.MicroKRWDenom, - assets.MicroUSDDenom, - assets.MicroCNYDenom, - assets.MicroJPYDenom, - assets.MicroGBPDenom, - assets.MicroEURDenom, - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ctx := input.ctx.WithBlockHeight(int64(i)) - - for j := 0; j < numOfValidators; j++ { - for d := 0; d < len(denoms); d++ { - salt := string(j) - bz, err := oracle.VoteHash(salt, sdk.OneDec(), denoms[d], sdk.ValAddress(addrs[j])) - if err != nil { - panic(err) - } - - prevoteMsg := oracle.NewMsgPricePrevote(hex.EncodeToString(bz), denoms[d], addrs[j], sdk.ValAddress(addrs[j])) - res := h(ctx, prevoteMsg) - if !res.IsOK() { - panic(res.Log) - } - - voteMsg := oracle.NewMsgPriceVote(sdk.OneDec(), salt, denoms[d], addrs[j], sdk.ValAddress(addrs[j])) - res = h(ctx.WithBlockHeight(1), voteMsg) - if !res.IsOK() { - panic(res.Log) - } - - } - } - - oracle.EndBlocker(ctx, input.oracleKeeper) - } -} diff --git a/x/bench/treasury_bench_test.go b/x/bench/treasury_bench_test.go deleted file mode 100644 index 542154b9e..000000000 --- a/x/bench/treasury_bench_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package bench - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/treasury" -) - -func BenchmarkTreasuryUpdatePerEpoch(b *testing.B) { - input := createTestInput() - - taxAmount := sdk.NewInt(1000).MulRaw(assets.MicroUnit) - - // Set random prices - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(1)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, sdk.NewDec(10)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, sdk.NewDec(100)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, sdk.NewDec(1000)) - - params := input.treasuryKeeper.GetParams(input.ctx) - probationEpoch := params.WindowProbation.Int64() - - b.ResetTimer() - for i := int64(0); i < int64(b.N)+probationEpoch; i++ { - - input.ctx = input.ctx.WithBlockHeight(i*util.BlocksPerEpoch - 1) - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt)) - - input.treasuryKeeper.RecordTaxProceeds(input.ctx, sdk.Coins{ - sdk.NewCoin(assets.MicroSDRDenom, taxAmount), - sdk.NewCoin(assets.MicroKRWDenom, taxAmount), - sdk.NewCoin(assets.MicroGBPDenom, taxAmount), - sdk.NewCoin(assets.MicroCNYDenom, taxAmount), - }) - - treasury.EndBlocker(input.ctx, input.treasuryKeeper) - } -} diff --git a/x/budget/client/cli/cli_test.go b/x/budget/client/cli/cli_test.go deleted file mode 100644 index 7daad5bcf..000000000 --- a/x/budget/client/cli/cli_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/spf13/cobra" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" - "github.com/terra-project/core/x/budget" - - "github.com/cosmos/cosmos-sdk/client" -) - -func TestSubmitProgramTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - budgetTxCmd := &cobra.Command{ - Use: "budget", - Short: "budget transaction subcommands", - } - - txCmd.AddCommand(budgetTxCmd) - - budgetTxCmd.AddCommand(client.PostCommands( - GetCmdSubmitProgram(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `budget`, - `submit-program`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--title=testprogram`, - `--description=testprogramtestprogram`, - `--executor=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestVoteTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - budgetTxCmd := &cobra.Command{ - Use: "budget", - Short: "budget transaction subcommands", - } - - txCmd.AddCommand(budgetTxCmd) - - budgetTxCmd.AddCommand(client.PostCommands( - GetCmdVote(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `budget`, - `vote`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--program-id=1`, - `--option=yes`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestWithdrawProgramTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - budgetTxCmd := &cobra.Command{ - Use: "budget", - Short: "budget transaction subcommands", - } - - txCmd.AddCommand(budgetTxCmd) - - budgetTxCmd.AddCommand(client.PostCommands( - GetCmdWithdrawProgram(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `budget`, - `withdraw`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--program-id=1`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestQueryProgram(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryProgramCmd := GetCmdQueryProgram(cdc) - - // Name check - require.Equal(t, budget.QueryProgram, queryProgramCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryProgramCmd.Args)) - - // Check Flags - programFlag := queryProgramCmd.Flag(flagProgramID) - require.NotNil(t, programFlag) - require.Equal(t, []string{"true"}, programFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryActives(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryActivesCmd := GetCmdQueryActives(cdc) - - // Name check - require.Equal(t, budget.QueryActiveList, queryActivesCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryActivesCmd.Args)) -} - -func TestQueryCandidates(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryCandidatesCmd := GetCmdQueryCandidates(cdc) - - // Name check - require.Equal(t, queryCandidatesCmd.Name(), budget.QueryCandidateList) - - // NoArg check - require.Equal(t, testutil.FS(queryCandidatesCmd.Args), testutil.FS(cobra.PositionalArgs(cobra.NoArgs))) -} - -func TestQueryVotes(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryVotesCmd := GetCmdQueryVotes(cdc) - - // Name check - require.Equal(t, budget.QueryVotes, queryVotesCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryVotesCmd.Args)) - - // Check Flags - programFlag := queryVotesCmd.Flag(flagProgramID) - require.NotNil(t, programFlag) - - voterFlag := queryVotesCmd.Flag(flagVoter) - require.NotNil(t, voterFlag) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParamsCmd := GetCmdQueryParams(cdc) - - // Name check - require.Equal(t, budget.QueryParams, queryParamsCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParamsCmd.Args)) -} diff --git a/x/budget/client/cli/query.go b/x/budget/client/cli/query.go deleted file mode 100644 index 3cfcf8dc3..000000000 --- a/x/budget/client/cli/query.go +++ /dev/null @@ -1,199 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - "strings" - - "github.com/spf13/viper" - - "github.com/terra-project/core/x/budget" - - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GetCmdQueryProgram implements the query program command. -func GetCmdQueryProgram(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "program", - Args: cobra.NoArgs, - Short: "Query details of a single program", - Long: strings.TrimSpace(` -Query details for a program. - -You can find the program-id of active programs by running terracli query budget actives -You can find the program-id of inactive (candidate) programs by running terracli query budget candidates - -$ terracli query budget program --program-id 1 -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - // validate that the program id is a uint - programIDStr := viper.GetString(flagProgramID) - if len(programIDStr) == 0 { - return fmt.Errorf("--program-id flag is required") - } - - programID, err := strconv.ParseUint(programIDStr, 10, 64) - if err != nil { - return fmt.Errorf("given program-id %s not a valid format\n, program-id should be formatted as integer", programIDStr) - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%d", budget.QuerierRoute, budget.QueryProgram, programID), nil) - if err != nil { - return err - } - - var program budget.Program - err = cdc.UnmarshalJSON(res, &program) - if err != nil { - return err - } - - return cliCtx.PrintOutput(program) - }, - } - - cmd.Flags().String(flagProgramID, "", "the program ID to query") - - cmd.MarkFlagRequired(flagProgramID) - - return cmd -} - -// GetCmdQueryActives implements a query actives command. -func GetCmdQueryActives(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: budget.QueryActiveList, - Args: cobra.NoArgs, - Short: "Query active programs", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryActiveList), nil) - if err != nil { - return err - } - - var actives budget.QueryActiveListResponse - cdc.MustUnmarshalJSON(res, &actives) - - return cliCtx.PrintOutput(actives) - }, - } - - return cmd -} - -// GetCmdQueryCandidates implements the query program candidates command. -func GetCmdQueryCandidates(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: budget.QueryCandidateList, - Args: cobra.NoArgs, - Short: "Query candidate programs", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryCandidateList), nil) - if err != nil { - return err - } - - var candidates budget.QueryCandidateListResponse - cdc.MustUnmarshalJSON(res, &candidates) - - return cliCtx.PrintOutput(candidates) - }, - } - - return cmd -} - -// GetCmdQueryVotes implements the command to query for program votes. -func GetCmdQueryVotes(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: budget.QueryVotes, - Args: cobra.NoArgs, - Short: "Query votes, filtered by voter and program id ", - Long: strings.TrimSpace(` -Query vote details filtered by voter address and program id. - -Example: -$ terracli query budget votes --program-id 1 --voter terra1nk5lsuvy0rcfjcdr8au8za0wq25rat0qa07p6t -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - params := budget.QueryVotesParams{} - - // Get voting address - voterAddrStr := viper.GetString(flagVoter) - if len(voterAddrStr) > 0 { - voterAddress, err := sdk.AccAddressFromBech32(voterAddrStr) - if err != nil { - return err - } - - params.Voter = voterAddress - } - - programIDStr := viper.GetString(flagProgramID) - - // validate that the program id is a uint - programID, err := strconv.ParseUint(programIDStr, 10, 64) - if err != nil { - return fmt.Errorf("program-id %s not a valid int, please input a valid program-id", programIDStr) - } - - params.ProgramID = programID - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryVotes), bz) - if err != nil { - return err - } - - var matchingVotes budget.QueryVotesResponse - cdc.MustUnmarshalJSON(res, &matchingVotes) - - return cliCtx.PrintOutput(matchingVotes) - }, - } - - cmd.Flags().String(flagProgramID, "0", "(optional) the program ID to query; defalut 0 for all programs") - cmd.Flags().String(flagVoter, "", "(optional) voter for the program") - - return cmd -} - -// GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: budget.QueryParams, - Args: cobra.NoArgs, - Short: "Query the current budget params", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryParams), nil) - if err != nil { - return err - } - - var params budget.Params - cdc.MustUnmarshalJSON(res, ¶ms) - return cliCtx.PrintOutput(params) - }, - } - - return cmd -} diff --git a/x/budget/client/cli/tx.go b/x/budget/client/cli/tx.go deleted file mode 100644 index 9f502c1da..000000000 --- a/x/budget/client/cli/tx.go +++ /dev/null @@ -1,307 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - - "github.com/terra-project/core/x/budget" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - - "encoding/json" - "io/ioutil" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -const ( - flagTitle = "title" - flagDescription = "description" - flagExecutor = "executor" - flagVoter = "voter" - flagProgram = "program" - flagProgramID = "program-id" - flagOption = "option" - flagOffline = "offline" -) - -type program struct { - Title string - Description string - Executor string -} - -var programFlags = []string{ - flagTitle, - flagDescription, - flagExecutor, -} - -// GetCmdSubmitProgram implements submitting a program transaction command. -func GetCmdSubmitProgram(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "submit-program", - Short: "Submit a program along with an initial deposit", - Long: strings.TrimSpace(` -Submit a program along with an initial deposit. program title, description, type and deposit can be given directly or through a program JSON file. For example: - -$ terracli budget submit-program --program="path/to/program.json" --from mykey - -where program.json contains: - -{ - "title": "Test program", - "description": "My awesome program (include a website link for impact)", - "executor": terra1nk5lsuvy0rcfjcdr8au8za0wq25rat0qa07p6t, -} - -is equivalent to - -$ terracli budget submit-program --title="Test program" --description="My awesome program" ... --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - program, err := parseSubmitProgramFlags() - if err != nil { - return err - } - - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - // Get from address - from := cliCtx.GetFromAddress() - - // Get executor address - executorAddr, err := sdk.AccAddressFromBech32(program.Executor) - if err != nil { - return err - } - - offline := viper.GetBool(flagOffline) - if !offline { - - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - - // Pull associated account - submitter, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - - submitterCoins := submitter.GetCoins() - - // Query params to get deposit amount - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryParams), nil) - if err != nil { - return err - } - - var params budget.Params - cdc.MustUnmarshalJSON(res, ¶ms) - - // Check submitter has enough coins to pay a deposit - if submitterCoins.AmountOf(params.Deposit.Denom).LT(params.Deposit.Amount) { - return fmt.Errorf(strings.TrimSpace(` - account %s has insufficient amount of coins to pay a deposit.\n - Required: %s\n - Given: %s\n`), - from, params.Deposit.String(), submitterCoins.String()) - } - - } - - msg := budget.NewMsgSubmitProgram(program.Title, program.Description, from, executorAddr) - err = msg.ValidateBasic() - if err != nil { - return err - } - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().String(flagTitle, "", "title of program") - cmd.Flags().String(flagDescription, "", "(optional) description of program") - cmd.Flags().String(flagExecutor, "", "executor of program") - cmd.Flags().String(flagProgram, "", "program file path (if this path is given, other program flags are ignored)") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -func parseSubmitProgramFlags() (*program, error) { - program := &program{} - programFile := viper.GetString(flagProgram) - - if len(programFile) == 0 { - program.Title = viper.GetString(flagTitle) - program.Description = viper.GetString(flagDescription) - program.Executor = viper.GetString(flagExecutor) - - // Check title existence - if len(program.Title) == 0 { - return nil, fmt.Errorf("--%s flag is required", flagTitle) - } - - // Check executor existence - if len(program.Executor) == 0 { - return nil, fmt.Errorf("--%s flag is required", flagExecutor) - } - - return program, nil - } - - for _, flag := range programFlags { - if len(viper.GetString(flag)) > 0 { - return nil, fmt.Errorf("--%s flag provided alongside --program, which is a noop", flag) - } - } - - contents, err := ioutil.ReadFile(programFile) - if err != nil { - return nil, err - } - - err = json.Unmarshal(contents, program) - if err != nil { - return nil, err - } - - return program, nil -} - -// GetCmdVote implements creating a new vote command. -func GetCmdVote(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "vote", - Short: "Vote for an candidate/active program, options: yes or no", - Long: strings.TrimSpace(` -Submit a vote for an candidate/active program. - -You can find the program-id of active programs by running terracli query budget actives -You can find the program-id of candidate programs by running terracli query budget candidates - -$ terracli tx budget vote --program-id 1 --option yes --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - // Get voting address - from := cliCtx.GetFromAddress() - - // Validate that the program id is a uint - programStrID := viper.GetString(flagProgramID) - programID, err := strconv.ParseUint(programStrID, 10, 64) - if err != nil { - return fmt.Errorf("given program-id {%s} is not a valid format; program-id should be formatted as integer", programStrID) - } - - // Find out which vote option user chose - var option bool - optionStr := viper.GetString(flagOption) - if optionStr == "yes" || optionStr == "true" { - option = true - } else if optionStr == "no" || optionStr == "false" { - option = false - } else { - return fmt.Errorf(`given option {%s} is not valid format;\n option should be formatted as "yes" or "no"`, optionStr) - } - - offline := viper.GetBool(flagOffline) - - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - } - - // Build vote message and run basic validation - msg := budget.NewMsgVoteProgram(programID, option, from) - err = msg.ValidateBasic() - if err != nil { - return err - } - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.MarkFlagRequired(flagProgramID) - cmd.MarkFlagRequired(flagOption) - - cmd.Flags().String(flagProgramID, "", "the program ID to vote") - cmd.Flags().String(flagOption, "", "yes or no") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -// GetCmdVote implements creating a new vote command. -func GetCmdWithdrawProgram(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "withdraw", - Short: "withdraw a program from consideration", - Long: strings.TrimSpace(` -Withdraw a program from consideration. The deposit is only refunded if the program is already in the active set. - -$ terracli tx budget withdraw --program-id 1 -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - // Get voting address - from := cliCtx.GetFromAddress() - - // validate that the program id is a uint - programStrID := viper.GetString(flagProgramID) - if programStrID == "" { - return fmt.Errorf("--program-id flag is required") - } - - programID, err := strconv.ParseUint(programStrID, 10, 64) - if err != nil { - return fmt.Errorf("given program-id %s not a valid int, please input a valid program-id", programStrID) - } - - offline := viper.GetBool(flagOffline) - - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - } - - // Build vote message and run basic validation - msg := budget.NewMsgWithdrawProgram(programID, from) - err = msg.ValidateBasic() - if err != nil { - return err - } - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.MarkFlagRequired(flagProgramID) - - cmd.Flags().String(flagProgramID, "", "the program ID to withdraw") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} diff --git a/x/budget/client/module_client.go b/x/budget/client/module_client.go deleted file mode 100644 index 5a92cae32..000000000 --- a/x/budget/client/module_client.go +++ /dev/null @@ -1,52 +0,0 @@ -package client - -import ( - "github.com/terra-project/core/x/budget/client/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - budgetQueryCmd := &cobra.Command{ - Use: "budget", - Short: "Querying commands for the budget module", - } - budgetQueryCmd.AddCommand(client.GetCommands( - cli.GetCmdQueryProgram(mc.cdc), - cli.GetCmdQueryActives(mc.cdc), - cli.GetCmdQueryCandidates(mc.cdc), - cli.GetCmdQueryVotes(mc.cdc), - cli.GetCmdQueryParams(mc.cdc), - )...) - - return budgetQueryCmd -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - budgetTxCmd := &cobra.Command{ - Use: "budget", - Short: "budget transaction subcommands", - } - - budgetTxCmd.AddCommand(client.PostCommands( - cli.GetCmdSubmitProgram(mc.cdc), - cli.GetCmdWithdrawProgram(mc.cdc), - cli.GetCmdVote(mc.cdc), - )...) - - return budgetTxCmd -} diff --git a/x/budget/client/module_client_test.go b/x/budget/client/module_client_test.go deleted file mode 100644 index ff43e894a..000000000 --- a/x/budget/client/module_client_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "active-list": true, - "candidate-list": true, - "params": true, - "program": true, - "votes": true, - } - - txCmdList = map[string]bool{ - "withdraw": true, - "vote": true, - "submit-program": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/budget/client/rest/query.go b/x/budget/client/rest/query.go deleted file mode 100644 index 6f4199ca6..000000000 --- a/x/budget/client/rest/query.go +++ /dev/null @@ -1,132 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/terra-project/core/x/budget" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/gorilla/mux" - "github.com/pkg/errors" -) - -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/budget/programs/actives", queryActivesHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/budget/programs/candidates", queryCandidatesHandlerFn(cdc, cliCtx)).Methods("GET") - - r.HandleFunc(fmt.Sprintf("/budget/programs/{%s}", RestProgramID), queryProgramHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/budget/programs/{%s}/votes", RestProgramID), queryVotesHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/budget/programs/{%s}/votes/{%s}", RestProgramID, RestVoter), queryVotesHandlerFn(cdc, cliCtx)).Methods("GET") - - r.HandleFunc("/budget/params", queryParamsHandlerFn(cdc, cliCtx)).Methods("GET") -} - -func queryProgramHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - strProgramID := vars[RestProgramID] - - if len(strProgramID) == 0 { - err := errors.New("programID required but not specified") - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", budget.QuerierRoute, budget.QueryProgram, strProgramID), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func queryActivesHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryActiveList), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func queryCandidatesHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryCandidateList), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// todo: Split this functionality into helper functions to remove the above -func queryVotesHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - strProgramID := vars[RestProgramID] - strVoterAddr := vars[RestVoter] - - params := budget.QueryVotesParams{} - - if len(strProgramID) == 0 { - err := errors.New("programID should be specified") - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - programID, ok := rest.ParseUint64OrReturnBadRequest(w, strProgramID) - if !ok { - return - } - - params.ProgramID = programID - - if len(strVoterAddr) != 0 { - voterAcc, err := sdk.AccAddressFromBech32(strVoterAddr) - if err != nil { - err := errors.New("voter address malformed") - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params.Voter = voterAcc - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryVotes), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryParams), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} diff --git a/x/budget/client/rest/rest.go b/x/budget/client/rest/rest.go deleted file mode 100644 index e39a02707..000000000 --- a/x/budget/client/rest/rest.go +++ /dev/null @@ -1,20 +0,0 @@ -package rest - -import ( - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/gorilla/mux" -) - -// REST Variable names -// nolint -const ( - RestProgramID = "program-id" - RestVoter = "voter" -) - -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - registerTxRoutes(cliCtx, r, cdc) - registerQueryRoutes(cliCtx, r, cdc) -} diff --git a/x/budget/client/rest/tx.go b/x/budget/client/rest/tx.go deleted file mode 100644 index 11e766bce..000000000 --- a/x/budget/client/rest/tx.go +++ /dev/null @@ -1,182 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - "strings" - - "github.com/terra-project/core/x/budget" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/gorilla/mux" - "github.com/pkg/errors" -) - -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/budget/programs/submit", submitProgramHandlerFn(cdc, cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/budget/programs/{%s}/withdraw", RestProgramID), withdrawProgramHandlerFn(cdc, cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/budget/programs/{%s}/votes", RestProgramID), voteHandlerFn(cdc, cliCtx)).Methods("POST") -} - -type submitProgramReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Title string `json:"title"` // Title of the Program - Description string `json:"description"` // Description of the Program - Executor sdk.AccAddress `json:"executor"` // Address of the executor -} - -type voteReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Option bool `json:"option"` // option from OptionSet chosen by the voter -} - -type withdrawReq struct { - BaseReq rest.BaseReq `json:"base_req"` -} - -func submitProgramHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req submitProgramReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - fromAddress, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - fromAccount, err := cliCtx.GetAccount(fromAddress) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - // Query params to get deposit amount - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", budget.QuerierRoute, budget.QueryParams), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - var params budget.Params - cdc.MustUnmarshalJSON(res, ¶ms) - - if fromAccount.GetCoins().AmountOf(params.Deposit.Denom).LT(params.Deposit.Amount) { - err := fmt.Errorf(strings.TrimSpace(` - account %s has insufficient amount of coins to pay the offered coins.\n - Required: %s\n - Given: %s\n`), fromAddress, params.Deposit, fromAccount.GetCoins()) - - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - } - - // create the message - msg := budget.NewMsgSubmitProgram(req.Title, req.Description, fromAddress, req.Executor) - err = msg.ValidateBasic() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -func withdrawProgramHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - strProgramID := vars[RestProgramID] - - if len(strProgramID) == 0 { - err := errors.New("programID required but not specified") - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - programID, ok := rest.ParseUint64OrReturnBadRequest(w, strProgramID) - if !ok { - return - } - - var req withdrawReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - fromAddress, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - // create the message - msg := budget.NewMsgWithdrawProgram(programID, fromAddress) - err = msg.ValidateBasic() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - programIDStr := vars[RestProgramID] - - if len(programIDStr) == 0 { - err := errors.New("programID required but not specified") - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - programID, ok := rest.ParseUint64OrReturnBadRequest(w, programIDStr) - if !ok { - return - } - - var req voteReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - fromAddress, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - // create the message - msg := budget.NewMsgVoteProgram(programID, req.Option, fromAddress) - err = msg.ValidateBasic() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} diff --git a/x/budget/codec.go b/x/budget/codec.go deleted file mode 100644 index 82f360b77..000000000 --- a/x/budget/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package budget - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var msgCdc = codec.New() - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgSubmitProgram{}, "budget/MsgSubmitProgram", nil) - cdc.RegisterConcrete(MsgWithdrawProgram{}, "budget/MsgWithdrawProgram", nil) - cdc.RegisterConcrete(MsgVoteProgram{}, "budget/MsgVoteProgram", nil) - - cdc.RegisterConcrete(&Program{}, "budget/Program", nil) -} - -func init() { - RegisterCodec(msgCdc) -} diff --git a/x/budget/constants.go b/x/budget/constants.go deleted file mode 100644 index b01b8a96e..000000000 --- a/x/budget/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package budget - -const ( - // ModuleName is the name of the budget module - ModuleName = "budget" - - // StoreKey is the string store representation - StoreKey = ModuleName - - // RouterKey is the msg router key for the budget module - RouterKey = ModuleName - - // QuerierRoute is the query router key for the budget module - QuerierRoute = ModuleName - - // DefaultParamspace is for the paramspace notation - DefaultParamspace = ModuleName -) diff --git a/x/budget/end_blocker.go b/x/budget/end_blocker.go deleted file mode 100644 index 80b686d62..000000000 --- a/x/budget/end_blocker.go +++ /dev/null @@ -1,148 +0,0 @@ -package budget - -import ( - "strconv" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/budget/tags" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Tally returns votePower = yesVotes minus NoVotes for program, as well as the total votes. -// Power is denominated in validator bonded tokens (Luna stake size) -func tally(ctx sdk.Context, k Keeper, targetProgramID uint64) (votePower sdk.Int, totalPower sdk.Int) { - votePower = sdk.ZeroInt() - totalPower = k.valset.TotalBondedTokens(ctx) - - targetProgramIDPrefix := keyVote(targetProgramID, sdk.AccAddress{}) - k.IterateVotesWithPrefix(ctx, targetProgramIDPrefix, func(programID uint64, voter sdk.AccAddress, option bool) (stop bool) { - valAddr := sdk.ValAddress(voter) - - if validator := k.valset.Validator(ctx, valAddr); validator != nil { - bondSize := validator.GetBondedTokens() - if option { - votePower = votePower.Add(bondSize) - } else { - votePower = votePower.Sub(bondSize) - } - } else { - k.DeleteVote(ctx, targetProgramID, voter) - } - - return false - }) - - return -} - -// clearsThreshold returns true if totalPower * threshold < votePower -func clearsThreshold(votePower, totalPower sdk.Int, threshold sdk.Dec) bool { - return votePower.GTE(threshold.MulInt(totalPower).RoundInt()) -} - -// EndBlocker is called at the end of every block -func EndBlocker(ctx sdk.Context, k Keeper) (resTags sdk.Tags) { - params := k.GetParams(ctx) - resTags = sdk.EmptyTags() - - k.CandQueueIterateExpired(ctx, ctx.BlockHeight(), func(programID uint64) (stop bool) { - program, err := k.GetProgram(ctx, programID) - if err != nil { - return false - } - - // Did not pass the tally, delete program - votePower, totalPower := tally(ctx, k, programID) - - if !clearsThreshold(votePower, totalPower, params.ActiveThreshold) { - k.DeleteVotesForProgram(ctx, programID) - k.DeleteProgram(ctx, programID) - resTags.AppendTag(tags.Action, tags.ActionProgramRejected) - } else { - resTags.AppendTag(tags.Action, tags.ActionProgramPassed) - } - - resTags.AppendTags( - sdk.NewTags( - tags.ProgramID, strconv.FormatUint(programID, 10), - tags.Weight, votePower.String(), - ), - ) - - k.CandQueueRemove(ctx, program.getVotingEndBlock(ctx, k), programID) - return false - }) - - // Time to re-weight programs - if util.IsPeriodLastBlock(ctx, params.VotePeriod) { - claims := types.ClaimPool{} - - // iterate programs and weight them - k.IteratePrograms(ctx, true, func(program Program) (stop bool) { - votePower, totalPower := tally(ctx, k, program.ProgramID) - - // Need to check if the program should be legacied - if !clearsThreshold(votePower, totalPower, params.LegacyThreshold) { - // Delete all votes on target program - k.DeleteVotesForProgram(ctx, program.ProgramID) - k.DeleteProgram(ctx, program.ProgramID) - resTags.AppendTag(tags.Action, tags.ActionProgramLegacied) - } else { - claims = append(claims, types.NewClaim(votePower, program.Executor)) - resTags.AppendTag(tags.Action, tags.ActionProgramGranted) - } - - resTags.AppendTags( - sdk.NewTags( - tags.ProgramID, strconv.FormatUint(program.ProgramID, 10), - tags.Weight, votePower.String(), - ), - ) - - return false - }) - - k.addClaimPool(ctx, claims) - } - - // Time to distribute rewards to claims - if util.IsPeriodLastBlock(ctx, util.BlocksPerEpoch) { - epoch := util.GetEpoch(ctx) - rewardWeight := k.tk.GetRewardWeight(ctx, epoch) - seigniorage := k.mk.PeekEpochSeigniorage(ctx, epoch) - rewardPool := sdk.OneDec().Sub(rewardWeight).MulInt(seigniorage) - - if rewardPool.GT(sdk.ZeroDec()) { - rewardPoolCoin, err := k.mrk.GetSwapDecCoin(ctx, sdk.NewDecCoinFromDec(assets.MicroLunaDenom, rewardPool), assets.MicroSDRDenom) - if err != nil { - // No SDR swap rate exists - rewardPoolCoin = sdk.NewDecCoinFromDec(assets.MicroLunaDenom, rewardPool) - } - - weightSum := sdk.ZeroInt() - k.iterateClaimPool(ctx, func(_ sdk.AccAddress, weight sdk.Int) (stop bool) { - weightSum = weightSum.Add(weight) - return false - }) - - k.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - rewardAmt := rewardPoolCoin.Amount.MulInt(weight).QuoInt(weightSum).TruncateInt() - - // never return err, but handle err for lint - err := k.mk.Mint(ctx, recipient, sdk.NewCoin(rewardPoolCoin.Denom, rewardAmt)) - if err != nil { - panic(err) - } - - return false - }) - } - - // Clear all claims - k.clearClaimPool(ctx) - } - return -} diff --git a/x/budget/end_blocker_test.go b/x/budget/end_blocker_test.go deleted file mode 100644 index 43b98c099..000000000 --- a/x/budget/end_blocker_test.go +++ /dev/null @@ -1,252 +0,0 @@ -package budget - -import ( - "math/rand" - "testing" - "time" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/mock" - "github.com/terra-project/core/types/util" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto/secp256k1" -) - -func TestEndBlockerTallyBasic(t *testing.T) { - input := createTestInput(t) - - // create test program - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - // Add validators and their votes; to keep things simple, let's assume each validator holds 1 token - valset := mock.NewMockValSet() - for _, addr := range addrs { - valAddr := sdk.ValAddress(addr) - validator := mock.NewMockValidator(valAddr, sdk.OneInt()) - valset.Validators = append(valset.Validators, validator) - - input.budgetKeeper.AddVote(input.ctx, testProgram.ProgramID, addr, true) - } - input.budgetKeeper.valset = valset - - actualVotePower, actualTotalPower := tally(input.ctx, input.budgetKeeper, testProgram.ProgramID) - - // totalPower and votepower should match the number of validators (uniform, single weighted) - require.Equal(t, actualTotalPower, sdk.NewInt(int64(len(addrs)))) - require.Equal(t, actualVotePower, sdk.NewInt(int64(len(addrs)))) -} - -func TestEndBlockerTallyRandom(t *testing.T) { - input := createTestInput(t) - - // create test program - - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - rand.Seed(int64(time.Now().Nanosecond())) - numValidators := rand.Int() % 100 // cap validator count by a 100 - - totalPower := 0 - votePower := 0 - valset := mock.NewMockValSet() - for i := 0; i < numValidators; i++ { - valAccAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - valAddr := sdk.ValAddress(valAccAddr) - valPower := rand.Int() % 10000 - validator := mock.NewMockValidator(valAddr, sdk.NewInt(int64(valPower))) - valset.Validators = append(valset.Validators, validator) - - totalPower += valPower - - option := rand.Int() % 2 - choice := option == 1 - if choice { - votePower += valPower - } else { - votePower -= valPower - } - - input.budgetKeeper.AddVote(input.ctx, testProgram.ProgramID, valAccAddr, choice) - } - input.budgetKeeper.valset = valset - - actualVotePower, actualTotalPower := tally(input.ctx, input.budgetKeeper, testProgram.ProgramID) - - require.Equal(t, actualTotalPower, sdk.NewInt(int64(totalPower))) - require.Equal(t, actualVotePower, sdk.NewInt(int64(votePower))) -} - -func TestEndBlockerTiming(t *testing.T) { - input := createTestInput(t) - - // create test program - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - // Add a vote each from validators - for _, addr := range addrs { - input.budgetKeeper.AddVote(input.ctx, testProgram.ProgramID, addr, true) - } - - // No claims should have been settled yet - EndBlocker(input.ctx, input.budgetKeeper) - - claimCount := countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 0, claimCount) - - // Advance block height by voteperiod - 1, and the program should be settled. - params := input.budgetKeeper.GetParams(input.ctx) - input.ctx = input.ctx.WithBlockHeight(params.VotePeriod - 1) - EndBlocker(input.ctx, input.budgetKeeper) - - claimCount = countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 1, claimCount) - - input.budgetKeeper.iterateClaimPool(input.ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - require.Equal(t, input.budgetKeeper.valset.TotalBondedTokens(input.ctx), weight) - return true - }) - -} - -func TestEndBlockerClaimDistribution(t *testing.T) { - input := createTestInput(t) - - // create test program - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - // Add a vote each from validators - for _, addr := range addrs { - input.budgetKeeper.AddVote(input.ctx, testProgram.ProgramID, addr, true) - } - - // No claims should have been settled yet - EndBlocker(input.ctx, input.budgetKeeper) - - claimCount := countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 0, claimCount) - - // Advance block height by voteperiod - 1, and the program should be settled. - params := input.budgetKeeper.GetParams(input.ctx) - input.ctx = input.ctx.WithBlockHeight(params.VotePeriod - 1) - EndBlocker(input.ctx, input.budgetKeeper) - - claimCount = countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 1, claimCount) - - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(1000))) - - // after 5 week, distribution date reach - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch*5 - 1) - input.treasuryKeeper.SetRewardWeight(input.ctx, sdk.NewDecWithPrec(1, 1)) - EndBlocker(input.ctx, input.budgetKeeper) - - claimCount = countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 0, claimCount) -} - -func TestEndBlockerLegacy(t *testing.T) { - input := createTestInput(t) - - defaultBudgetParams := DefaultParams() - defaultBudgetParams.VotePeriod = 1 - input.budgetKeeper.SetParams(input.ctx, defaultBudgetParams) - - ctx := input.ctx.WithBlockHeight(1) - - // Create test program - testProgram := generateTestProgram(ctx, input.budgetKeeper) - - input.budgetKeeper.StoreProgram(ctx, testProgram) - - // Add a vote each from validators - for _, addr := range addrs { - input.budgetKeeper.AddVote(ctx, testProgram.ProgramID, addr, true) - } - - // Claims should have been settled - EndBlocker(ctx, input.budgetKeeper) - claimCount := countClaimPool(input.ctx, input.budgetKeeper) - require.Equal(t, 1, claimCount) - - ctx = input.ctx.WithBlockHeight(2) - - for _, addr := range addrs { - input.budgetKeeper.AddVote(ctx, testProgram.ProgramID, addr, false) - } - - // Program should be legacy - EndBlocker(ctx, input.budgetKeeper) - _, err := input.budgetKeeper.GetProgram(ctx, testProgram.ProgramID) - require.Error(t, err) -} - -func TestEndBlockerPassOrReject(t *testing.T) { - input := createTestInput(t) - - // add a hundred validators with 1 stakable token each - valset := mock.NewMockValSet() - valAddrs := []sdk.AccAddress{} - for i := 0; i < 100; i++ { - valAccAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - validator := mock.NewMockValidator(sdk.ValAddress(valAccAddr), sdk.OneInt()) - valset.Validators = append(valset.Validators, validator) - valAddrs = append(valAddrs, valAccAddr) - } - input.budgetKeeper.valset = valset - - // Compute minimum validator support - activeThreshold := input.budgetKeeper.GetParams(input.ctx).ActiveThreshold - minNumTokensToPass := activeThreshold.MulInt(sdk.NewInt(100)).TruncateInt() - - // create test program - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - input.budgetKeeper.CandQueueInsert(input.ctx, testProgram.getVotingEndBlock(input.ctx, input.budgetKeeper), testProgram.ProgramID) - - // vote slightly such that the sum falls short of the threshold; tally should fail and program not activated. - for i := 0; i < int(minNumTokensToPass.Int64())-1; i++ { - input.budgetKeeper.AddVote(input.ctx, testProgram.ProgramID, valAddrs[i], true) - } - - params := input.budgetKeeper.GetParams(input.ctx) - input.ctx = input.ctx.WithBlockHeight(params.VotePeriod) - EndBlocker(input.ctx, input.budgetKeeper) - _, err := input.budgetKeeper.GetProgram(input.ctx, testProgram.ProgramID) - require.NotNil(t, err) - - input.budgetKeeper.DeleteProgram(input.ctx, testProgram.ProgramID) - - // vote above the threshold; the tally should now pass - testProgram2 := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram2) - input.budgetKeeper.CandQueueInsert(input.ctx, testProgram2.getVotingEndBlock(input.ctx, input.budgetKeeper), testProgram2.ProgramID) - - for i := 0; i < int(minNumTokensToPass.Int64())+1; i++ { - input.budgetKeeper.AddVote(input.ctx, testProgram2.ProgramID, valAddrs[i], true) - } - - input.ctx = input.ctx.WithBlockHeight(params.VotePeriod) - EndBlocker(input.ctx, input.budgetKeeper) - _, err = input.budgetKeeper.GetProgram(input.ctx, testProgram2.ProgramID) - require.Nil(t, err) -} - -func countClaimPool(ctx sdk.Context, keeper Keeper) (claimCount int) { - keeper.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - claimCount++ - return false - }) - - return claimCount -} diff --git a/x/budget/errors.go b/x/budget/errors.go deleted file mode 100644 index dae0a6414..000000000 --- a/x/budget/errors.go +++ /dev/null @@ -1,71 +0,0 @@ -package budget - -import ( - "fmt" - "strconv" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - // DefaultCodespace nolint - DefaultCodespace sdk.CodespaceType = "budget" - - // Budget errors - CodeInvalidProgramID sdk.CodeType = 1 - CodeInvalidTitle sdk.CodeType = 2 - CodeInvalidDescription sdk.CodeType = 3 - CodeProgramNotFound sdk.CodeType = 4 - CodeVoteNotFound sdk.CodeType = 5 - CodeInvalidSubmitter sdk.CodeType = 6 - CodeRefundFailed sdk.CodeType = 7 - CodeInvalidSubmitBlockHeight sdk.CodeType = 8 - CodeDuplicateProgramID sdk.CodeType = 9 -) - -// nolint -func ErrInvalidTitle() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidTitle, "Cannot submit a program with empty title") -} - -// nolint -func ErrInvalidDescription() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidDescription, "Cannot submit a program with empty description") -} - -// nolint -func ErrProgramNotFound(ProgramID uint64) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeProgramNotFound, "program with id "+ - strconv.Itoa(int(ProgramID))+" not found") -} - -// nolint -func ErrInvalidProgramID(ProgramID uint64) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidProgramID, "program id "+ - strconv.Itoa(int(ProgramID))+" invalid. Must be an uint") -} - -// nolint -func ErrVoteNotFound() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeVoteNotFound, "Vote not found") -} - -// nolint -func ErrInvalidSubmitter(submitter sdk.AccAddress) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidSubmitter, fmt.Sprintf("Submitter does not match %s", submitter)) -} - -// nolint -func ErrRefundFailed(submitter sdk.AccAddress, programID uint64) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeRefundFailed, fmt.Sprintf("Refund failed to %s of program %d", submitter.String(), programID)) -} - -// nolint -func ErrInvalidSubmitBlockHeight(submitBlock int64) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidSubmitBlockHeight, fmt.Sprintf("Submit Block should be 0 at genesis not %d", submitBlock)) -} - -// nolint -func ErrDuplicateProgramID(programID uint64) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeDuplicateProgramID, fmt.Sprintf("program ID is duplicated %d", programID)) -} diff --git a/x/budget/expected_keepers.go b/x/budget/expected_keepers.go deleted file mode 100644 index 3afa9c2fe..000000000 --- a/x/budget/expected_keepers.go +++ /dev/null @@ -1,23 +0,0 @@ -package budget - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// expected mint keeper -type MintKeeper interface { - Mint(ctx sdk.Context, recipient sdk.AccAddress, coin sdk.Coin) (err sdk.Error) - Burn(ctx sdk.Context, payer sdk.AccAddress, coin sdk.Coin) (err sdk.Error) - PeekEpochSeigniorage(ctx sdk.Context, epoch sdk.Int) (epochSeigniorage sdk.Int) -} - -// expected treasury keeper -type TreasuryKeeper interface { - GetRewardWeight(ctx sdk.Context, epoch sdk.Int) (rewardWeight sdk.Dec) - SetRewardWeight(ctx sdk.Context, weight sdk.Dec) -} - -// expected market keeper -type MarketKeeper interface { - GetSwapDecCoin(ctx sdk.Context, offerCoin sdk.DecCoin, askDenom string) (sdk.DecCoin, sdk.Error) -} diff --git a/x/budget/genesis.go b/x/budget/genesis.go deleted file mode 100644 index d54e04576..000000000 --- a/x/budget/genesis.go +++ /dev/null @@ -1,132 +0,0 @@ -package budget - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState - all distribution state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params"` // budget params - - ActivePrograms Programs `json:"active_programs"` - CandidatePrograms Programs `json:"candidate_programs"` - - Votes Votes `json:"votes"` -} - -func NewGenesisState(params Params, activePrograms, - candidatePrograms Programs, votes Votes) GenesisState { - return GenesisState{ - Params: params, - - ActivePrograms: activePrograms, - CandidatePrograms: candidatePrograms, - Votes: votes, - } -} - -// get raw genesis raw message for testing -func DefaultGenesisState() GenesisState { - return GenesisState{ - Params: DefaultParams(), - - ActivePrograms: Programs{}, - CandidatePrograms: Programs{}, - Votes: Votes{}, - } -} - -// new oracle genesis -func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { - keeper.SetParams(ctx, data.Params) - - for _, program := range data.ActivePrograms { - keeper.StoreProgram(ctx, program) - } - - for _, program := range data.CandidatePrograms { - keeper.StoreProgram(ctx, program) - keeper.CandQueueInsert(ctx, data.Params.VotePeriod, program.ProgramID) - } - - for _, vote := range data.Votes { - keeper.AddVote(ctx, vote.ProgramID, vote.Voter, vote.Option) - } - -} - -// ExportGenesis returns a GenesisState for a given context and keeper. The -// GenesisState will contain the pool, and validator/delegator distribution info's -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { - params := keeper.GetParams(ctx) - - var activePrograms Programs - keeper.IteratePrograms(ctx, true, func(program Program) (stop bool) { - activePrograms = append(activePrograms, program) - return false - }) - - var candidatePrograms Programs - keeper.CandQueueIterate(ctx, func(programID uint64) (stop bool) { - program, err := keeper.GetProgram(ctx, programID) - if err != nil { - return false - } - - candidatePrograms = append(candidatePrograms, program) - return false - }) - - var votes Votes - keeper.IterateVotes(ctx, func(programID uint64, voterAddr sdk.AccAddress, option bool) (stop bool) { - votes = append(votes, NewVote(programID, option, voterAddr)) - return false - }) - - return NewGenesisState(params, activePrograms, candidatePrograms, votes) -} - -// ValidateGenesis validates the provided oracle genesis state to ensure the -// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) -func ValidateGenesis(data GenesisState) error { - err := validateParams(data.Params) - - if err != nil { - return err - } - - var programMap map[uint64]bool - for _, program := range data.ActivePrograms { - if program.SubmitBlock != 0 { - return ErrInvalidSubmitBlockHeight(program.SubmitBlock) - } - - // duplicate program ID check - if _, ok := programMap[program.ProgramID]; ok { - return ErrDuplicateProgramID(program.ProgramID) - } - - programMap[program.ProgramID] = true - } - - for _, program := range data.CandidatePrograms { - if program.SubmitBlock != 0 { - return ErrInvalidSubmitBlockHeight(program.SubmitBlock) - } - - // duplicate program ID check - if _, ok := programMap[program.ProgramID]; ok { - return ErrDuplicateProgramID(program.ProgramID) - } - - programMap[program.ProgramID] = true - } - - for _, vote := range data.Votes { - if _, ok := programMap[vote.ProgramID]; !ok { - return ErrProgramNotFound(vote.ProgramID) - } - } - - return nil -} diff --git a/x/budget/handler.go b/x/budget/handler.go deleted file mode 100644 index bec7666e2..000000000 --- a/x/budget/handler.go +++ /dev/null @@ -1,121 +0,0 @@ -package budget - -import ( - "reflect" - "strconv" - - "github.com/terra-project/core/x/budget/tags" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// NewHandler creates a new handler for all budget type messages. -func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case MsgSubmitProgram: - return handleMsgSubmitProgram(ctx, k, msg) - case MsgWithdrawProgram: - return handleMsgWithdrawProgram(ctx, k, msg) - case MsgVoteProgram: - return handleMsgVoteProgram(ctx, k, msg) - - default: - errMsg := "Unrecognized budget Msg type: " + reflect.TypeOf(msg).Name() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// handleMsgVoteProgram handles the logic of a MsgSubmitProgram -func handleMsgSubmitProgram(ctx sdk.Context, k Keeper, msg MsgSubmitProgram) sdk.Result { - - // Subtract coins from the submitter balance and updates it - depositErr := k.PayDeposit(ctx, msg.Submitter) - if depositErr != nil { - return depositErr.Result() - } - - // Create and add program - programID := k.NewProgramID(ctx) - program := NewProgram( - programID, - msg.Title, - msg.Description, - msg.Submitter, - msg.Executor, - ctx.BlockHeight(), - ) - - k.StoreProgram(ctx, program) - k.CandQueueInsert(ctx, program.getVotingEndBlock(ctx, k), programID) - - return sdk.Result{ - Tags: sdk.NewTags( - tags.ProgramID, strconv.FormatUint(programID, 10), - ), - } -} - -// handleMsgWithdrawProgram handles the logic of a MsgWithdrawProgram -func handleMsgWithdrawProgram(ctx sdk.Context, k Keeper, msg MsgWithdrawProgram) sdk.Result { - program, err := k.GetProgram(ctx, msg.ProgramID) - if err != nil { - return ErrProgramNotFound(msg.ProgramID).Result() - } - - // Only submitters can withdraw the program submission - if !program.Submitter.Equals(msg.Submitter) { - return ErrInvalidSubmitter(msg.Submitter).Result() - } - - // Remove from candidate queue if not yet active - prgmEndBlock := program.getVotingEndBlock(ctx, k) - if k.CandQueueHas(ctx, prgmEndBlock, msg.ProgramID) { - k.CandQueueRemove(ctx, prgmEndBlock, msg.ProgramID) - err := k.RefundDeposit(ctx, program.Submitter) - - if err != nil { - return ErrRefundFailed(msg.Submitter, msg.ProgramID).Result() - } - } - - // Delete all votes on target program - k.DeleteVotesForProgram(ctx, msg.ProgramID) - k.DeleteProgram(ctx, msg.ProgramID) - - return sdk.Result{ - Tags: sdk.NewTags( - tags.ProgramID, strconv.FormatUint(msg.ProgramID, 10), - ), - } -} - -// handleMsgVoteProgram handles the logic of a MsgVoteProgram -func handleMsgVoteProgram(ctx sdk.Context, k Keeper, msg MsgVoteProgram) sdk.Result { - resTags := sdk.NewTags() - - _, err := k.GetProgram(ctx, msg.ProgramID) - if err != nil { - return ErrProgramNotFound(msg.ProgramID).Result() - } - - // Check the voter is a validator - val := k.valset.Validator(ctx, sdk.ValAddress(msg.Voter)) - if val == nil { - return staking.ErrNoDelegatorForAddress(DefaultCodespace).Result() - } - - k.AddVote(ctx, msg.ProgramID, msg.Voter, msg.Option) - - return sdk.Result{ - Tags: resTags.AppendTags( - sdk.NewTags( - tags.ProgramID, strconv.FormatUint(msg.ProgramID, 10), - tags.Voter, msg.Voter.String(), - tags.Option, strconv.FormatBool(msg.Option), - ), - ), - } -} diff --git a/x/budget/handler_test.go b/x/budget/handler_test.go deleted file mode 100644 index 3bbb596cc..000000000 --- a/x/budget/handler_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package budget - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestHandlerMsgSubmitProgram(t *testing.T) { - input := createTestInput(t) - - h := NewHandler(input.budgetKeeper) - - // Regular submit msg passes - msg := NewMsgSubmitProgram("test", "testdescription", addrs[0], addrs[1]) - res := h(input.ctx, msg) - require.True(t, res.IsOK()) - - // Everything else should be tested in validateMsg ... so skip -} - -func TestHandlerMsgWithdrawProgram(t *testing.T) { - input := createTestInput(t) - - h := NewHandler(input.budgetKeeper) - - // Submit program - submitMsg := NewMsgSubmitProgram("test", "testdescription", addrs[0], addrs[1]) - res := h(input.ctx, submitMsg) - require.True(t, res.IsOK()) - - // Withdrawing submitted program works - withdrawMsg := NewMsgWithdrawProgram(1, addrs[0]) - res = h(input.ctx, withdrawMsg) - require.True(t, res.IsOK()) - - // Withdrawing again doesn't work - withdrawMsg = NewMsgWithdrawProgram(1, addrs[0]) - res = h(input.ctx, withdrawMsg) - require.False(t, res.IsOK()) - - // Withdrawing from a different submitter address doesn't work - withdrawMsg = NewMsgWithdrawProgram(1, addrs[2]) - res = h(input.ctx, withdrawMsg) - require.False(t, res.IsOK()) - - // Withdrawing an unsubmitted program doesn't work - withdrawMsg = NewMsgWithdrawProgram(4, addrs[2]) - res = h(input.ctx, withdrawMsg) - require.False(t, res.IsOK()) -} - -func TestHandlerMsgVoteCandidate(t *testing.T) { - input := createTestInput(t) - - h := NewHandler(input.budgetKeeper) - - // Submit program - submitMsg := NewMsgSubmitProgram("test", "testdescription", addrs[0], addrs[1]) - res := h(input.ctx, submitMsg) - require.True(t, res.IsOK()) - - // Voting on a submitted program works - voteMsg := NewMsgVoteProgram(1, true, addrs[0]) - res = h(input.ctx, voteMsg) - require.True(t, res.IsOK()) - - // Voting on an un submitted program doesn't work - voteMsg = NewMsgVoteProgram(4, true, addrs[0]) - res = h(input.ctx, voteMsg) - require.False(t, res.IsOK()) -} diff --git a/x/budget/keeper.go b/x/budget/keeper.go deleted file mode 100644 index 5afc4456f..000000000 --- a/x/budget/keeper.go +++ /dev/null @@ -1,311 +0,0 @@ -package budget - -import ( - "strconv" - "strings" - - "github.com/terra-project/core/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// nolint -type Keeper struct { - cdc *codec.Codec // Codec to encore/decode structs - key sdk.StoreKey // Key to our module's store - valset sdk.ValidatorSet // Needed to compute voting power. - - mrk MarketKeeper // Needed to handle claims. This module only requires read swap rate between SDR and LUNA - mk MintKeeper // Needed to handle deposits. This module only requires read/writes to Terra balance and read seigniorage - tk TreasuryKeeper // Needed to handle claims. This module only requires read current reward weight - paramSpace params.Subspace -} - -// NewKeeper crates a new keeper -func NewKeeper(cdc *codec.Codec, - key sdk.StoreKey, - mrk MarketKeeper, - mk MintKeeper, - tk TreasuryKeeper, - valset sdk.ValidatorSet, - paramspace params.Subspace) Keeper { - return Keeper{ - cdc: cdc, - key: key, - mrk: mrk, - mk: mk, - tk: tk, - valset: valset, - paramSpace: paramspace.WithKeyTable(paramKeyTable()), - } -} - -//----------------------------------- -// Vote logic - -// GetVote returns the given option of a Program stored in the keeper -func (k Keeper) GetVote(ctx sdk.Context, programID uint64, voter sdk.AccAddress) (res bool, err sdk.Error) { - store := ctx.KVStore(k.key) - if bz := store.Get(keyVote(programID, voter)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) - } else { - err = ErrVoteNotFound() - } - return -} - -// AddVote adds the vote option to the store -func (k Keeper) AddVote(ctx sdk.Context, programID uint64, voter sdk.AccAddress, option bool) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(option) - store.Set(keyVote(programID, voter), bz) -} - -// DeleteVote deletes the vote from the store -func (k Keeper) DeleteVote(ctx sdk.Context, programID uint64, voter sdk.AccAddress) { - store := ctx.KVStore(k.key) - store.Delete(keyVote(programID, voter)) -} - -// DeleteVotesForProgram deletes the votes for the program from the store -func (k Keeper) DeleteVotesForProgram(ctx sdk.Context, programID uint64) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, keyVote(programID, sdk.AccAddress{})) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - store.Delete(iter.Key()) - } -} - -// IterateVotes iterates votes in the store -func (k Keeper) IterateVotes(ctx sdk.Context, handler func(uint64, sdk.AccAddress, bool) (stop bool)) { - k.IterateVotesWithPrefix(ctx, prefixVote, handler) -} - -// IterateVotesWithPrefix iterates votes with given {prefix} in the store -func (k Keeper) IterateVotesWithPrefix(ctx sdk.Context, prefix []byte, handler func(uint64, sdk.AccAddress, bool) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefix) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - voteKey := string(iter.Key()) - var option bool - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &option) - - elems := strings.Split(voteKey, ":") - programID, err := strconv.ParseUint(elems[1], 10, 0) - if err != nil { - continue - } - - voterAddrStr := elems[2] - voterAddr, err := sdk.AccAddressFromBech32(voterAddrStr) - if err != nil { - continue - } - - if handler(programID, voterAddr, option) { - break - } - } -} - -//----------------------------------- -// Deposit logic - -// PayDeposit pays the deposit by withdrawing from the submitter's balance. -func (k Keeper) PayDeposit(ctx sdk.Context, submitter sdk.AccAddress) (err sdk.Error) { - deposit := k.GetParams(ctx).Deposit - err = k.mk.Burn(ctx, submitter, deposit) - return -} - -// RefundDeposit refunds the deposit, by crediting the submitter's balance. -func (k Keeper) RefundDeposit(ctx sdk.Context, submitter sdk.AccAddress) (err sdk.Error) { - deposit := k.GetParams(ctx).Deposit - err = k.mk.Mint(ctx, submitter, deposit) - return -} - -//----------------------------------- -// Params logic - -// GetParams get budget params from the global param store -func (k Keeper) GetParams(ctx sdk.Context) Params { - var resultParams Params - k.paramSpace.Get(ctx, paramStoreKeyParams, &resultParams) - return resultParams -} - -// SetParams set budget params from the global param store -func (k Keeper) SetParams(ctx sdk.Context, params Params) { - k.paramSpace.Set(ctx, paramStoreKeyParams, ¶ms) -} - -//----------------------------------- -// Program logic - -// NewProgramID generates a new program id; advances sequentially from 1; 0 conflits with vote querier -func (k Keeper) NewProgramID(ctx sdk.Context) (programID uint64) { - store := ctx.KVStore(k.key) - if bz := store.Get(keyNextProgramID); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &programID) - programID++ - } else { - programID = 1 - } - - bz := k.cdc.MustMarshalBinaryLengthPrefixed(programID) - store.Set(keyNextProgramID, bz) - return -} - -// GetProgram gets the Program with the given id from the store. -func (k Keeper) GetProgram(ctx sdk.Context, programID uint64) (res Program, err sdk.Error) { - store := ctx.KVStore(k.key) - - if bz := store.Get(keyProgram(programID)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) - } else { - err = ErrProgramNotFound(programID) - } - return -} - -// StoreProgram sets a Program to the store -func (k Keeper) StoreProgram(ctx sdk.Context, program Program) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(program) - store.Set(keyProgram(program.ProgramID), bz) -} - -// DeleteProgram deletes a program from the store -func (k Keeper) DeleteProgram(ctx sdk.Context, programID uint64) { - store := ctx.KVStore(k.key) - store.Delete(keyProgram(programID)) -} - -// IteratePrograms iterates programs in the store -func (k Keeper) IteratePrograms(ctx sdk.Context, filterInactive bool, handler func(Program) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixProgram) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - - var program Program - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &program) - - // Filter out candidate programs if filterInactive is true - if filterInactive && k.CandQueueHas(ctx, program.getVotingEndBlock(ctx, k), program.ProgramID) { - continue - } - - if handler(program) { - break - } - - } -} - -//----------------------------------- -// Candidate Queue logic - -// CandQueueIterate iterate all the Programs in the candidate queue -func (k Keeper) CandQueueIterate(ctx sdk.Context, handler func(uint64) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixCandQueue) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var programID uint64 - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &programID) - - if handler(programID) { - break - } - } -} - -// CandQueueIterateExpired iterate all the Programs in the candidate queue that have outspent their voteperiod -func (k Keeper) CandQueueIterateExpired(ctx sdk.Context, endBlock int64, handler func(uint64) (stop bool)) { - store := ctx.KVStore(k.key) - iter := store.Iterator(prefixCandQueue, sdk.PrefixEndBytes(prefixCandQueueEndBlock(endBlock))) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var programID uint64 - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &programID) - - if handler(programID) { - break - } - } -} - -// CandQueueInsert Inserts a ProgramID into the Candidate Program queue at endTime -func (k Keeper) CandQueueInsert(ctx sdk.Context, endBlock int64, programID uint64) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(programID) - store.Set(keyCandidate(endBlock, programID), bz) -} - -// CandQueueHas Checks if a progrma exists in accordance with the given parameters -func (k Keeper) CandQueueHas(ctx sdk.Context, endBlock int64, programID uint64) (res bool) { - store := ctx.KVStore(k.key) - bz := store.Get(keyCandidate(endBlock, programID)) - return bz != nil -} - -// CandQueueRemove removes a ProgramID from the Candidate Program Queue -func (k Keeper) CandQueueRemove(ctx sdk.Context, endBlock int64, programID uint64) { - store := ctx.KVStore(k.key) - store.Delete(keyCandidate(endBlock, programID)) -} - -//----------------------------------- -// Claim pool logic - -// Iterate over oracle reward claims in the store -func (k Keeper) iterateClaimPool(ctx sdk.Context, handler func(recipient sdk.AccAddress, weight sdk.Int) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixClaim) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - recipientAddress := strings.Split(string(iter.Key()), ":")[1] - recipient, _ := sdk.AccAddressFromBech32(recipientAddress) - - var weight sdk.Int - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &weight) - if handler(recipient, weight) { - break - } - } -} - -// addClaimPool adds a claim to the the claim pool in the store -func (k Keeper) addClaimPool(ctx sdk.Context, pool types.ClaimPool) { - store := ctx.KVStore(k.key) - - for _, claim := range pool { - storeKeyClaim := keyClaim(claim.Recipient) - b := store.Get(storeKeyClaim) - weight := claim.Weight - if b != nil { - var prevWeight sdk.Int - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &prevWeight) - - weight = weight.Add(prevWeight) - } - b = k.cdc.MustMarshalBinaryLengthPrefixed(weight) - store.Set(storeKeyClaim, b) - } -} - -// clearClaimPool clears the claim pool from the store -func (k Keeper) clearClaimPool(ctx sdk.Context) { - store := ctx.KVStore(k.key) - k.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - store.Delete(keyClaim(recipient)) - return false - }) -} diff --git a/x/budget/keeper_keys.go b/x/budget/keeper_keys.go deleted file mode 100644 index a4af04bed..000000000 --- a/x/budget/keeper_keys.go +++ /dev/null @@ -1,47 +0,0 @@ -package budget - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// nolint -var ( - keyDelimiter = []byte(":") - keyNextProgramID = []byte("new-program-id") - - prefixProgram = []byte("program") - prefixVote = []byte("vote") - prefixCandQueue = []byte("candidate-queue") - prefixClaim = []byte("claim") - - paramStoreKeyParams = []byte("params") -) - -func keyProgram(programID uint64) []byte { - return []byte(fmt.Sprintf("%s:%d", prefixProgram, programID)) -} - -func keyVote(programID uint64, voterAddr sdk.AccAddress) []byte { - return []byte(fmt.Sprintf("%s:%d:%s", prefixVote, programID, voterAddr)) -} - -func prefixCandQueueEndBlock(endBlock int64) []byte { - return []byte(fmt.Sprintf("%s:%020d", prefixCandQueue, endBlock)) -} - -func keyCandidate(endBlock int64, programID uint64) []byte { - return []byte(fmt.Sprintf("%s:%020d:%d", prefixCandQueue, endBlock, programID)) -} - -func keyClaim(recipient sdk.AccAddress) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixClaim, recipient)) -} - -func paramKeyTable() params.KeyTable { - return params.NewKeyTable( - paramStoreKeyParams, Params{}, - ) -} diff --git a/x/budget/keeper_test.go b/x/budget/keeper_test.go deleted file mode 100644 index 932aab3ea..000000000 --- a/x/budget/keeper_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package budget - -import ( - "math/rand" - "testing" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/terra-project/core/types" -) - -func TestKeeperProgramID(t *testing.T) { - input := createTestInput(t) - - // Program ids start at 0 and increment by 1 on each request - numTests := 10 - for i := 0; i < numTests; i++ { - id := input.budgetKeeper.NewProgramID(input.ctx) - require.Equal(t, id, uint64(i+1)) - } -} - -func TestKeeperDeposit(t *testing.T) { - input := createTestInput(t) - - // Set the balance to equal the default deposit - deposit := sdk.Coins{input.budgetKeeper.GetParams(input.ctx).Deposit} - err := input.bankKeeper.SetCoins(input.ctx, addrs[0], deposit) - require.Nil(t, err) - - // addr0 has enough coins to pay the deposit - err = input.budgetKeeper.PayDeposit(input.ctx, addrs[0]) - require.Nil(t, err) - - // Doesn't have enough coins to pay the deposit - err = input.budgetKeeper.PayDeposit(input.ctx, addrs[0]) - require.NotNil(t, err) - - // Refund works - err = input.budgetKeeper.RefundDeposit(input.ctx, addrs[0]) - require.Nil(t, err) - - // After refund, addr0's balance equals the deposit he paid previously - balance := input.bankKeeper.GetCoins(input.ctx, addrs[0]) - require.Equal(t, balance, deposit) -} - -func TestKeeperParams(t *testing.T) { - input := createTestInput(t) - - defaultParams := DefaultParams() - input.budgetKeeper.SetParams(input.ctx, defaultParams) - - retrievedParams := input.budgetKeeper.GetParams(input.ctx) - require.Equal(t, defaultParams, retrievedParams) -} - -func TestKeeperProgram(t *testing.T) { - input := createTestInput(t) - - maxTests := 30 - idCeiling := 10 - - // We create a program bitmap to keep track of programs that have been stored / - // deleted from the context store. We compare the bitmap to the store at the end - // of the test to verify state correctness. - programBitmap := make([]bool, idCeiling) - - // just a random test program... - - rand.Seed(int64(time.Now().Nanosecond())) - numTests := rand.Int() % maxTests - for i := 0; i < numTests; i++ { - programID := uint64(rand.Int63() % int64(idCeiling)) - testProgram := NewProgram(programID, "", "", addrs[0], addrs[1], 0) - action := rand.Int() % 2 - if action == 0 { - programBitmap[programID] = true - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - } else { - programBitmap[programID] = false - input.budgetKeeper.DeleteProgram(input.ctx, programID) - } - } - - // Count live programs in the bitmap - expectedLivePrgmCount := 0 - for programID, live := range programBitmap { - if live { - expectedLivePrgmCount++ - - // Make sure bitmap positives are also reflected in the store - _, err := input.budgetKeeper.GetProgram(input.ctx, uint64(programID)) - require.Nil(t, err) - } - } - - actualLivePrgmCount := 0 - input.budgetKeeper.IteratePrograms(input.ctx, false, func(program Program) (stop bool) { - require.True(t, programBitmap[program.ProgramID]) - actualLivePrgmCount++ - return false - }) - - // Count of live programs should match in the context store and bitmap - require.Equal(t, expectedLivePrgmCount, actualLivePrgmCount) -} - -func TestKeeperVote(t *testing.T) { - input := createTestInput(t) - - maxTests := 30 - idCeiling := 10 - voterCeiling := 10 - - voters := make([]sdk.AccAddress, voterCeiling) - for i := 0; i < voterCeiling; i++ { - voters = append(voters, sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())) - } - - // We create a vote bitmap to keep track of programs that have been stored / - // deleted from the context store. We compare the bitmap to the store at the end - // of the test to verify state correctness. - voteBitmap := make([]map[string]bool, idCeiling) - for j := 0; j < idCeiling; j++ { - voteBitmap[j] = make(map[string]bool) - } - - rand.Seed(int64(time.Now().Nanosecond())) - numTests := rand.Int() % maxTests - for i := 0; i < numTests; i++ { - programID := uint64(rand.Int63() % int64(idCeiling)) - voterIndex := uint64(rand.Int() % voterCeiling) - voterAddress := voters[voterIndex] - - action := rand.Int() % 2 - if action == 0 { - voteBitmap[programID][voterAddress.String()] = true - input.budgetKeeper.AddVote(input.ctx, programID, voterAddress, true) - } else { - voteBitmap[programID][voterAddress.String()] = false - input.budgetKeeper.DeleteVote(input.ctx, programID, voterAddress) - } - } - - // Count live programs in the bitmap - expectedLiveVoteCount := 0 - for programID, votes := range voteBitmap { - for voterAddrStr, live := range votes { - voterAddr, err := sdk.AccAddressFromBech32(voterAddrStr) - require.Nil(t, err) - - if live { - expectedLiveVoteCount++ - - _, err2 := input.budgetKeeper.GetVote(input.ctx, uint64(programID), voterAddr) - require.Nil(t, err2) - } - } - } - - // Match live programs in the store - actualLiveVoteCount := 0 - input.budgetKeeper.IterateVotes(input.ctx, - func(programID uint64, voterAddress sdk.AccAddress, option bool) (stop bool) { - require.True(t, voteBitmap[programID][voterAddress.String()]) - actualLiveVoteCount++ - return false - }) - - // Count of live programs should match in the context store and bitmap - require.Equal(t, expectedLiveVoteCount, actualLiveVoteCount) -} - -func TestKeeperCandidateQueue(t *testing.T) { - input := createTestInput(t) - - // Insert a program in the queue - input.budgetKeeper.CandQueueInsert(input.ctx, 0, 0) - - // Check if it exists; it should. - exists := input.budgetKeeper.CandQueueHas(input.ctx, 0, 0) - require.True(t, exists) - - // Not with a different endblock num though. - exists = input.budgetKeeper.CandQueueHas(input.ctx, 1, 0) - require.False(t, exists) - - // Nor with a different programID. - exists = input.budgetKeeper.CandQueueHas(input.ctx, 0, 1) - require.False(t, exists) - - // delete works too. - input.budgetKeeper.CandQueueRemove(input.ctx, 0, 0) - exists = input.budgetKeeper.CandQueueHas(input.ctx, 0, 0) - require.False(t, exists) - - // test iterator - numTests := 30 - counter := 0 - for i := 0; i < numTests; i++ { - programID := input.budgetKeeper.NewProgramID(input.ctx) - input.budgetKeeper.CandQueueInsert(input.ctx, int64(i), programID) - input.ctx = input.ctx.WithBlockHeight(int64(i)) - input.budgetKeeper.CandQueueIterateExpired(input.ctx, input.ctx.BlockHeight(), - func(programID uint64) (stop bool) { - counter++ - - input.budgetKeeper.CandQueueRemove(input.ctx, input.ctx.BlockHeight(), programID) - return false - }) - } - - require.Equal(t, numTests, counter) -} - -func TestKeeperClaimPool(t *testing.T) { - input := createTestInput(t) - - // Test addClaimPool - claim := types.NewClaim(sdk.NewInt(10), addrs[0]) - claim2 := types.NewClaim(sdk.NewInt(20), addrs[1]) - claimPool := types.ClaimPool{claim, claim2} - input.budgetKeeper.addClaimPool(input.ctx, claimPool) - - claim = types.NewClaim(sdk.NewInt(15), addrs[0]) - claim2 = types.NewClaim(sdk.NewInt(30), addrs[2]) - claimPool = types.ClaimPool{claim, claim2} - input.budgetKeeper.addClaimPool(input.ctx, claimPool) - - // Test iterateClaimPool - input.budgetKeeper.iterateClaimPool(input.ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - if recipient.Equals(addrs[0]) { - require.Equal(t, sdk.NewInt(25), weight) - } else if recipient.Equals(addrs[1]) { - require.Equal(t, sdk.NewInt(20), weight) - } else if recipient.Equals(addrs[2]) { - require.Equal(t, sdk.NewInt(30), weight) - } - return false - }) -} diff --git a/x/budget/msg.go b/x/budget/msg.go deleted file mode 100644 index bba6cbb19..000000000 --- a/x/budget/msg.go +++ /dev/null @@ -1,174 +0,0 @@ -package budget - -import ( - "fmt" - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// MsgSubmitProgram defines a message to create a Program -type MsgSubmitProgram struct { - Title string `json:"title"` // Title of the Program - Description string `json:"description"` // Description of the Program - Submitter sdk.AccAddress `json:"submitter"` // Address of the submitter - Executor sdk.AccAddress `json:"executor"` // Address of the executor -} - -// NewMsgSubmitProgram submits a message with a new Program -func NewMsgSubmitProgram(title string, description string, - submitter sdk.AccAddress, executor sdk.AccAddress) MsgSubmitProgram { - return MsgSubmitProgram{ - Title: title, - Description: description, - Submitter: submitter, - Executor: executor, - } -} - -// Route returns msg route -func (msg MsgSubmitProgram) Route() string { return "budget" } - -// Type returns msg type -func (msg MsgSubmitProgram) Type() string { return "submitprogram" } - -// GetSignBytes returns sign byptes -func (msg MsgSubmitProgram) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) -} - -// GetSigners returns signer -func (msg MsgSubmitProgram) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Submitter} -} - -// ValidateBasic validate msg -func (msg MsgSubmitProgram) ValidateBasic() sdk.Error { - if len(msg.Submitter) == 0 { - return sdk.ErrInvalidAddress("Invalid address: " + msg.Submitter.String()) - } - if len(msg.Executor) == 0 { - return sdk.ErrInvalidAddress("Invalid address: " + msg.Executor.String()) - } - if len(strings.TrimSpace(msg.Title)) <= 0 { - return ErrInvalidTitle() - } - if len(strings.TrimSpace(msg.Description)) <= 0 { - return ErrInvalidDescription() - } - - return nil -} - -// String stringify the msg -func (msg MsgSubmitProgram) String() string { - return fmt.Sprintf(`MsgSubmitProgram - Title: %v - Submitter: %v - Executor: %v`, msg.Title, msg.Submitter, msg.Executor) -} - -//-------------------------------------------------------- -//-------------------------------------------------------- - -// WithdrawProgramMsg defines the msg of a staker containing the vote option to an -// specific Program -type MsgWithdrawProgram struct { - ProgramID uint64 `json:"program_id"` // ID of the Program - Submitter sdk.AccAddress `json:"submitter"` // Address of the voter -} - -// NewMsgWithdrawProgram creates a VoteMsg instance -func NewMsgWithdrawProgram(programID uint64, submitter sdk.AccAddress) MsgWithdrawProgram { - return MsgWithdrawProgram{ - ProgramID: programID, - Submitter: submitter, - } -} - -// Route returns msg route -func (msg MsgWithdrawProgram) Route() string { return "budget" } - -// Type returns msg type -func (msg MsgWithdrawProgram) Type() string { return "withdraw" } - -// GetSignBytes returns sign byptes -func (msg MsgWithdrawProgram) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) -} - -// GetSigners returns signer -func (msg MsgWithdrawProgram) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Submitter} -} - -// ValidateBasic validate msg -func (msg MsgWithdrawProgram) ValidateBasic() sdk.Error { - if len(msg.Submitter) == 0 { - return sdk.ErrInvalidAddress("Invalid address: " + msg.Submitter.String()) - } - return nil -} - -// String stringify the msg -func (msg MsgWithdrawProgram) String() string { - return fmt.Sprintf(`MsgWithdrawProgram - ProgramID: %v - Submitter: %v`, msg.ProgramID, msg.Submitter) -} - -//-------------------------------------------------------- -//-------------------------------------------------------- - -// MsgVoteProgram defines the msg of a staker containing the vote option to an -// specific Program -type MsgVoteProgram struct { - ProgramID uint64 `json:"program_id"` // ID of the Program - Option bool `json:"option"` // Option chosen by voter - Voter sdk.AccAddress `json:"voter"` // Address of the voter -} - -// NewMsgVoteProgram creates a MsgVoteProgram instance -func NewMsgVoteProgram(programID uint64, option bool, voter sdk.AccAddress) MsgVoteProgram { - return MsgVoteProgram{ - ProgramID: programID, - Option: option, - Voter: voter, - } -} - -// Route returns msg route -func (msg MsgVoteProgram) Route() string { return "budget" } - -// Type returns msg type -func (msg MsgVoteProgram) Type() string { return "vote" } - -// GetSignBytes returns sign byptes -func (msg MsgVoteProgram) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) -} - -// GetSigners returns signer -func (msg MsgVoteProgram) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Voter} -} - -// ValidateBasic validate msg -func (msg MsgVoteProgram) ValidateBasic() sdk.Error { - if len(msg.Voter) == 0 { - return sdk.ErrInvalidAddress("Invalid address: " + msg.Voter.String()) - } - if msg.ProgramID == 0 { - return ErrInvalidProgramID(msg.ProgramID) - } - - return nil -} - -// String stringify the msg -func (msg MsgVoteProgram) String() string { - return fmt.Sprintf(`MsgVoteProgram - ProgramID: %v - Voter: %v - Option: %v`, msg.ProgramID, msg.Voter, msg.Option) -} diff --git a/x/budget/params.go b/x/budget/params.go deleted file mode 100644 index 1f49fe65f..000000000 --- a/x/budget/params.go +++ /dev/null @@ -1,63 +0,0 @@ -package budget - -import ( - "fmt" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Params budget parameters -type Params struct { - ActiveThreshold sdk.Dec `json:"active_threshold"` // threshold of vote that will transition a program open -> active budget queue - LegacyThreshold sdk.Dec `json:"legacy_threshold"` // threshold of vote that will transition a program active -> legacy budget queue - VotePeriod int64 `json:"vote_period"` // vote period - Deposit sdk.Coin `json:"deposit"` // Minimum deposit in TerraSDR -} - -// NewParams creates a new param instance -func NewParams(activeThreshold sdk.Dec, legacyThreshold sdk.Dec, votePeriod int64, deposit sdk.Coin) Params { - return Params{ - ActiveThreshold: activeThreshold, - LegacyThreshold: legacyThreshold, - VotePeriod: votePeriod, - Deposit: deposit, - } -} - -// DefaultParams creates default budget module parameters -func DefaultParams() Params { - return NewParams( - sdk.NewDecWithPrec(1, 1), // 10% - sdk.NewDecWithPrec(0, 2), // 0% - util.BlocksPerMonth, - sdk.NewInt64Coin(assets.MicroSDRDenom, sdk.NewInt(100).MulRaw(assets.MicroUnit).Int64()), - ) -} - -func validateParams(params Params) error { - if params.ActiveThreshold.LTE(sdk.ZeroDec()) { - return fmt.Errorf("budget active threshold should be greater than 0, is %s", params.ActiveThreshold.String()) - } - if params.LegacyThreshold.LT(sdk.ZeroDec()) { - return fmt.Errorf("budget legacy threshold should be greater than or equal to 0, is %s", params.LegacyThreshold.String()) - } - if params.VotePeriod <= 0 { - return fmt.Errorf("budget parameter VotePeriod must be > 0, is %d", params.VotePeriod) - } - if params.Deposit.Amount.LTE(sdk.ZeroInt()) { - return fmt.Errorf("budget parameter Deposit must be > 0, is %v", params.Deposit.String()) - } - return nil -} - -func (params Params) String() string { - return fmt.Sprintf(`Budget Params: - ActiveThreshold: %s - LegacyThreshold: %s - VotePeriod: %d - Deposit: %s - `, params.ActiveThreshold, params.LegacyThreshold, params.VotePeriod, params.Deposit) -} diff --git a/x/budget/program.go b/x/budget/program.go deleted file mode 100644 index 5bd3e3a82..000000000 --- a/x/budget/program.go +++ /dev/null @@ -1,62 +0,0 @@ -package budget - -import ( - "fmt" - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Program defines the basic properties of a staking Program -type Program struct { - ProgramID uint64 `json:"program_id"` // ID of the Program - Title string `json:"title"` // Title of the Program - Description string `json:"description"` // Description of the Program - Submitter sdk.AccAddress `json:"submitter"` // Validator address of the proposer - Executor sdk.AccAddress `json:"executor"` // Account address of the executor - SubmitBlock int64 `json:"submit_time"` // Block height from which the Program is open for votations -} - -// NewProgram validates deposit and creates a new Program -func NewProgram( - programID uint64, - title string, - description string, - submitter sdk.AccAddress, - executor sdk.AccAddress, - submitBlock int64) Program { - return Program{ - ProgramID: programID, - Title: title, - Description: description, - Submitter: submitter, - Executor: executor, - SubmitBlock: submitBlock, - } -} - -func (p *Program) getVotingEndBlock(ctx sdk.Context, k Keeper) int64 { - return p.SubmitBlock + k.GetParams(ctx).VotePeriod -} - -// String implements fmt.Stringer -func (p Program) String() string { - return fmt.Sprintf(`Program - ProgramID: %d - Title: %s - Description: %s - Submitter: %v - Executor: %v - SubmitBlock: %d`, - p.ProgramID, p.Title, p.Description, p.Submitter, p.Executor, p.SubmitBlock) -} - -// Programs is a collection of Program -type Programs []Program - -func (p Programs) String() (out string) { - for _, val := range p { - out += val.String() + "\n" - } - return strings.TrimSpace(out) -} diff --git a/x/budget/querier.go b/x/budget/querier.go deleted file mode 100644 index a1416698c..000000000 --- a/x/budget/querier.go +++ /dev/null @@ -1,195 +0,0 @@ -package budget - -import ( - "strconv" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" -) - -// query endpoints supported by the governance Querier -const ( - QueryProgram = "program" - QueryVotes = "votes" - QueryActiveList = "active-list" - QueryCandidateList = "candidate-list" - QueryParams = "params" -) - -// NewQuerier is the module level router for state queries -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case QueryProgram: - return queryProgram(ctx, path[1:], req, keeper) - case QueryVotes: - return queryVotes(ctx, req, keeper) - case QueryActiveList: - return queryActiveList(ctx, req, keeper) - case QueryCandidateList: - return queryCandidateList(ctx, req, keeper) - case QueryParams: - return queryParams(ctx, req, keeper) - default: - return nil, sdk.ErrUnknownRequest("unknown oracle query endpoint") - } - } -} - -// nolint: unparam -func queryProgram(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - programIDStr := path[0] - programIDInt, strConvertError := strconv.Atoi(programIDStr) - if strConvertError != nil { - return nil, sdk.ErrInternal("ProgramID must be a valid int") - } - - programID := uint64(programIDInt) - program, pErr := keeper.GetProgram(ctx, programID) - if pErr != nil { - return nil, pErr - } - - bz, err := codec.MarshalJSONIndent(keeper.cdc, program) - if err != nil { - return nil, sdk.ErrInternal("could not marshal result to JSON") - } - - return bz, nil -} - -// Params for query 'custom/oracle/votes' -type QueryVotesParams struct { - Voter sdk.AccAddress - ProgramID uint64 -} - -// JSON response format -type QueryVotesResponse struct { - Votes Votes `json:"votes"` -} - -func (r QueryVotesResponse) String() (out string) { - out = r.Votes.String() - return strings.TrimSpace(out) -} - -// creates a new instance of QueryVoteParams -func NewQueryVotesParams(voter sdk.AccAddress, programID uint64) QueryVotesParams { - return QueryVotesParams{ - Voter: voter, - ProgramID: programID, - } -} - -// nolint: unparam -func queryVotes(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - var params QueryVotesParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) - } - - filteredVotes := Votes{} - prefix := prefixVote - handler := func(programID uint64, voter sdk.AccAddress, option bool) (stop bool) { - vote := NewVote(programID, option, voter) - filteredVotes = append(filteredVotes, vote) - - return false - } - - if params.ProgramID != 0 && !params.Voter.Empty() { - prefix = keyVote(params.ProgramID, params.Voter) - } else if params.ProgramID != 0 { - prefix = keyVote(params.ProgramID, sdk.AccAddress{}) - } else if !params.Voter.Empty() { - handler = func(programID uint64, voter sdk.AccAddress, option bool) (stop bool) { - if params.Voter.Equals(voter) { - vote := NewVote(programID, option, voter) - filteredVotes = append(filteredVotes, vote) - } - - return false - } - } - - keeper.IterateVotesWithPrefix(ctx, prefix, handler) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryVotesResponse{Votes: filteredVotes}) - if err != nil { - panic("could not marshal result to JSON") - } - - return bz, nil -} - -// JSON response format -type QueryActiveListResponse struct { - Actives Programs `json:"actives"` -} - -func (r QueryActiveListResponse) String() (out string) { - out = r.Actives.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryActiveList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - - programs := Programs{} - keeper.IteratePrograms(ctx, true, func(program Program) (stop bool) { - programs = append(programs, program) - return false - }) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryActiveListResponse{Actives: programs}) - if err != nil { - panic("could not marshal result to JSON") - } - - return bz, nil -} - -// JSON response format -type QueryCandidateListResponse struct { - Candidates Programs `json:"candidates"` -} - -func (r QueryCandidateListResponse) String() (out string) { - out = r.Candidates.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryCandidateList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - programs := Programs{} - - keeper.CandQueueIterate(ctx, func(programID uint64) (stop bool) { - program, err := keeper.GetProgram(ctx, programID) - if err != nil { - return false - } - - programs = append(programs, program) - return false - }) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryCandidateListResponse{Candidates: programs}) - if err != nil { - panic("could not marshal result to JSON") - } - - return bz, nil -} - -func queryParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} diff --git a/x/budget/querier_test.go b/x/budget/querier_test.go deleted file mode 100644 index 4c5c1692f..000000000 --- a/x/budget/querier_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package budget - -import ( - "strconv" - "strings" - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const custom = "custom" - -func getQueriedProgram(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, programID uint64) Program { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryProgram}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryProgram, strconv.FormatUint(programID, 10)}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var program Program - err2 := cdc.UnmarshalJSON(bz, &program) - require.Nil(t, err2) - - return program -} - -func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, voter sdk.AccAddress, programID uint64) Votes { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryVotes}, "/"), - Data: cdc.MustMarshalJSON(NewQueryVotesParams(voter, programID)), - } - - bz, err := querier(ctx, []string{QueryVotes}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryVotesResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.Votes -} - -func getQueriedActiveList(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) Programs { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryActiveList}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryActiveList}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryActiveListResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.Actives -} - -func getQueriedCandidateList(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) Programs { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryCandidateList}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryCandidateList}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryCandidateListResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.Candidates -} - -func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) Params { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryParams}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryParams}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var params Params - err2 := cdc.UnmarshalJSON(bz, ¶ms) - require.Nil(t, err2) - - return params -} - -func TestQueryParams(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.budgetKeeper) - - params := DefaultParams() - input.budgetKeeper.SetParams(input.ctx, params) - - queriedParams := getQueriedParams(t, input.ctx, input.cdc, querier) - - require.Equal(t, queriedParams, params) -} - -func TestQueryProgram(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.budgetKeeper) - - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - queriedProgram := getQueriedProgram(t, input.ctx, input.cdc, querier, testProgram.ProgramID) - - require.Equal(t, queriedProgram, testProgram) -} - -func TestQueryVotes(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.budgetKeeper) - - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - var votes Votes - for _, addr := range addrs { - vote := NewVote(testProgram.ProgramID, true, addr) - votes = append(votes, vote) - - input.budgetKeeper.AddVote(input.ctx, vote.ProgramID, vote.Voter, vote.Option) - } - - // queriedVotes without filter - queriedVotes := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.AccAddress{}, 0) - require.Equal(t, len(queriedVotes), len(votes)) - - // queriedVotes with programID filter - queriedVotesWithProgramID := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.AccAddress{}, testProgram.ProgramID) - require.Equal(t, len(queriedVotesWithProgramID), len(votes)) - - // queriedVotes with voter filter - queriedVotesWithVoter := getQueriedVotes(t, input.ctx, input.cdc, querier, addrs[0], 0) - require.Equal(t, queriedVotesWithVoter, votes[:1]) - - // queriedVotes with programID and voter filter - queriedVotesWithBoth := getQueriedVotes(t, input.ctx, input.cdc, querier, addrs[1], testProgram.ProgramID) - require.Equal(t, queriedVotesWithBoth, votes[1:2]) -} - -func TestQueryActiveList(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.budgetKeeper) - - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - - queriedActiveList := getQueriedActiveList(t, input.ctx, input.cdc, querier) - - require.Equal(t, queriedActiveList, Programs{testProgram}) -} - -func TestQueryCandidateList(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.budgetKeeper) - - testProgram := generateTestProgram(input.ctx, input.budgetKeeper) - input.budgetKeeper.StoreProgram(input.ctx, testProgram) - input.budgetKeeper.CandQueueInsert(input.ctx, 0, testProgram.ProgramID) - - queriedCandidateList := getQueriedCandidateList(t, input.ctx, input.cdc, querier) - - require.Equal(t, queriedCandidateList, Programs{testProgram}) -} diff --git a/x/budget/tags/tags.go b/x/budget/tags/tags.go deleted file mode 100644 index 0144c8d68..000000000 --- a/x/budget/tags/tags.go +++ /dev/null @@ -1,22 +0,0 @@ -package tags - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Governance tags -var ( - ActionProgramLegacied = "program-legacied" - ActionProgramPassed = "program-passed" - ActionProgramRejected = "program-rejected" - ActionProgramGranted = "program-grant" - - Action = sdk.TagAction - Submitter = "submitter" - ProgramID = "program-id" - VotingPeriodStart = "voting-period-start" - Executor = "executor" - Voter = "voter" - Weight = "weight" - Option = "option" -) diff --git a/x/budget/test_common.go b/x/budget/test_common.go deleted file mode 100644 index 2e1a9f643..000000000 --- a/x/budget/test_common.go +++ /dev/null @@ -1,239 +0,0 @@ -package budget - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/treasury" - - "time" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - pubKeys = []crypto.PubKey{ - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - } - - addrs = []sdk.AccAddress{ - sdk.AccAddress(pubKeys[0].Address()), - sdk.AccAddress(pubKeys[1].Address()), - sdk.AccAddress(pubKeys[2].Address()), - } - - valConsPubKeys = []crypto.PubKey{ - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - } - - valConsAddrs = []sdk.ConsAddress{ - sdk.ConsAddress(valConsPubKeys[0].Address()), - sdk.ConsAddress(valConsPubKeys[1].Address()), - sdk.ConsAddress(valConsPubKeys[2].Address()), - } - - uSDRAmt = sdk.NewInt(1005 * assets.MicroUnit) - uLunaAmt = sdk.NewInt(10 * assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - cdc *codec.Codec - mintKeeper mint.Keeper - bankKeeper bank.Keeper - budgetKeeper Keeper - treasuryKeeper TreasuryKeeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyBudget := sdk.NewKVStoreKey(StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - keyTreasury := sdk.NewKVStoreKey(treasury.StoreKey) - keyMarket := sdk.NewKVStoreKey(market.StoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyBudget, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyTreasury, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - - if err := ms.LoadLatestVersion(); err != nil { - require.Nil(t, err) - } - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingParams := staking.DefaultParams() - stakingParams.BondDenom = assets.MicroLunaDenom - stakingKeeper.SetParams(ctx, stakingParams) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := market.NewKeeper( - cdc, - keyMarket, - oracleKeeper, - mintKeeper, - paramsKeeper.Subspace(market.DefaultParamspace), - ) - - treasuryKeeper := treasury.NewKeeper( - cdc, - keyTreasury, - stakingKeeper.GetValidatorSet(), - mintKeeper, - marketKeeper, - paramsKeeper.Subspace(treasury.DefaultParamspace), - ) - - sh := staking.NewHandler(stakingKeeper) - for i, addr := range addrs { - err := mintKeeper.Mint(ctx, addr, sdk.NewCoin(assets.MicroSDRDenom, uSDRAmt)) - err2 := mintKeeper.Mint(ctx, addr, sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt)) - - require.NoError(t, err) - require.NoError(t, err2) - - // Add validators - commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(sdk.ValAddress(addr), valConsPubKeys[i], - sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt), staking.Description{}, commission, sdk.OneInt()) - res := sh(ctx, msg) - require.True(t, res.IsOK()) - - distrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(addr)) - staking.EndBlocker(ctx, stakingKeeper) - } - - budgetKeeper := NewKeeper( - cdc, - keyBudget, - marketKeeper, - mintKeeper, - treasuryKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(DefaultParamspace), - ) - - InitGenesis(ctx, budgetKeeper, DefaultGenesisState()) - - return testInput{ctx, cdc, mintKeeper, bankKeeper, budgetKeeper, treasuryKeeper} -} - -func generateTestProgram(ctx sdk.Context, budgetKeeper Keeper, accounts ...sdk.AccAddress) Program { - submitter := addrs[0] - if len(accounts) > 0 { - submitter = accounts[0] - } - - executor := addrs[1] - if len(accounts) > 1 { - executor = accounts[1] - } - - testProgramID := budgetKeeper.NewProgramID(ctx) - - return NewProgram(testProgramID, "testTitle", "testDescription", submitter, executor, util.GetEpoch(ctx).Int64()) -} diff --git a/x/budget/vote.go b/x/budget/vote.go deleted file mode 100644 index da759ad26..000000000 --- a/x/budget/vote.go +++ /dev/null @@ -1,34 +0,0 @@ -package budget - -import ( - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// no-lint -type Vote MsgVoteProgram - -func (v Vote) String() string { - return MsgVoteProgram(v).String() -} - -// NewMsgVoteProgram creates a MsgVoteProgram instance -func NewVote(programID uint64, option bool, voter sdk.AccAddress) Vote { - return Vote( - MsgVoteProgram{ - ProgramID: programID, - Option: option, - Voter: voter, - }) -} - -// Votes is a collection of Vote -type Votes []Vote - -func (v Votes) String() (out string) { - for _, val := range v { - out += val.String() + "\n" - } - return strings.TrimSpace(out) -} diff --git a/x/crisis/alias.go b/x/crisis/alias.go new file mode 100644 index 000000000..54b97d21b --- /dev/null +++ b/x/crisis/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/crisis/internal/types/ +package crisis + +import ( + "github.com/terra-project/core/x/crisis/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/crisis/cosmos_alias.go b/x/crisis/cosmos_alias.go new file mode 100644 index 000000000..071fce93f --- /dev/null +++ b/x/crisis/cosmos_alias.go @@ -0,0 +1,39 @@ +// nolint +package crisis + +import ( + "github.com/cosmos/cosmos-sdk/x/crisis" +) + +const ( + DefaultCodespace = crisis.DefaultCodespace + CodeInvalidInput = crisis.CodeInvalidInput + ModuleName = crisis.ModuleName + DefaultParamspace = crisis.DefaultParamspace +) + +var ( + // functions aliases + ErrNilSender = crisis.ErrNilSender + ErrUnknownInvariant = crisis.ErrUnknownInvariant + NewGenesisState = crisis.NewGenesisState + DefaultGenesisState = crisis.DefaultGenesisState + NewMsgVerifyInvariant = crisis.NewMsgVerifyInvariant + ParamKeyTable = crisis.ParamKeyTable + NewInvarRoute = crisis.NewInvarRoute + NewKeeper = crisis.NewKeeper + NewCosmosAppModule = crisis.NewAppModule + + // variable aliases + CosmosModuleCdc = crisis.ModuleCdc + ParamStoreKeyConstantFee = crisis.ParamStoreKeyConstantFee +) + +type ( + GenesisState = crisis.GenesisState + MsgVerifyInvariant = crisis.MsgVerifyInvariant + InvarRoute = crisis.InvarRoute + Keeper = crisis.Keeper + CosmosAppModule = crisis.AppModule + CosmosAppModuleBasic = crisis.AppModuleBasic +) diff --git a/x/crisis/internal/types/codec.go b/x/crisis/internal/types/codec.go new file mode 100644 index 000000000..c0e683cb4 --- /dev/null +++ b/x/crisis/internal/types/codec.go @@ -0,0 +1,21 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/crisis" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(crisis.MsgVerifyInvariant{}, "crisis/MsgVerifyInvariant", nil) +} + +// generic sealed codec to be used throughout module +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/crisis/module.go b/x/crisis/module.go new file mode 100644 index 000000000..dc5ae9f31 --- /dev/null +++ b/x/crisis/module.go @@ -0,0 +1,128 @@ +package crisis + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + core "github.com/terra-project/core/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + // customize to set default genesis state constant fee denom to luna + defaultGenesisState := DefaultGenesisState() + defaultGenesisState.ConstantFee.Denom = core.MicroLunaDenom + + return ModuleCdc.MustMarshalJSON(defaultGenesisState) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module for bank +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper *Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/distribution/alias.go b/x/distribution/alias.go new file mode 100644 index 000000000..0a1c6d908 --- /dev/null +++ b/x/distribution/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/distribution/internal/types/ +package distribution + +import ( + "github.com/terra-project/core/x/distribution/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/distribution/client/cli/cli_test.go b/x/distribution/client/cli/cli_test.go deleted file mode 100644 index 041b2d417..000000000 --- a/x/distribution/client/cli/cli_test.go +++ /dev/null @@ -1,182 +0,0 @@ -package cli - -import ( - "strings" - "testing" - - "github.com/spf13/cobra" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" - "github.com/terra-project/core/x/budget" - - "github.com/cosmos/cosmos-sdk/client" - dt "github.com/cosmos/cosmos-sdk/x/distribution" - dtk "github.com/cosmos/cosmos-sdk/x/distribution/keeper" -) - -func TestWithdrawRewardsTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - distTxCmd := &cobra.Command{ - Use: "distr", - Short: "Distribution transactions subcommands", - } - - txCmd.AddCommand(distTxCmd) - - distTxCmd.AddCommand(client.PostCommands( - GetCmdWithdrawRewards(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `distr`, - `withdraw-rewards`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--validator=terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldcl4phj`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestSetWithdrawAddrTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - distTxCmd := &cobra.Command{ - Use: "distr", - Short: "Distribution transactions subcommands", - } - - txCmd.AddCommand(distTxCmd) - - distTxCmd.AddCommand(client.PostCommands( - GetCmdSetWithdrawAddr(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `distr`, - `set-withdraw-addr`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--withdraw-to=terra13pqzy3n7ekfnpt9gmk9xtulzl49qw7td0hrsgh`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestWithdrawAllRewardsTx(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - withdrawAllRewardsCmd := GetCmdWithdrawAllRewards(cdc, dt.QuerierRoute) - - // Name check - require.Equal(t, withdrawAllRewardsCmd.Name(), "withdraw-all-rewards") - - // NoArg check - require.Equal(t, testutil.FS(withdrawAllRewardsCmd.Args), testutil.FS(cobra.PositionalArgs(cobra.NoArgs))) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParamsCmd := GetCmdQueryParams(dt.QuerierRoute, cdc) - - // Name check - require.Equal(t, queryParamsCmd.Name(), budget.QueryParams) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParamsCmd.Args)) -} - -func TestQueryValidatorOutstandingRewards(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorOutstandingRewardsCmd := GetCmdQueryValidatorOutstandingRewards(dt.QuerierRoute, cdc) - - // Name check - require.Equal(t, dtk.QueryValidatorOutstandingRewards, strings.ReplaceAll(queryValidatorOutstandingRewardsCmd.Name(), "-", "_")) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorOutstandingRewardsCmd.Args)) - - // Check Flags - validatorFlag := queryValidatorOutstandingRewardsCmd.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryValidatorCommission(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorCommission := GetCmdQueryValidatorCommission(dt.QuerierRoute, cdc) - - // Name check - require.Equal(t, dtk.QueryValidatorCommission, strings.ReplaceAll(queryValidatorCommission.Name(), "-", "_")) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorCommission.Args)) - - // Check Flags - validatorFlag := queryValidatorCommission.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryValidatorSlashes(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorSlashes := GetCmdQueryValidatorSlashes(dt.QuerierRoute, cdc) - - // Name check - require.Equal(t, dtk.QueryValidatorSlashes, strings.ReplaceAll(queryValidatorSlashes.Name(), "-", "_")) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorSlashes.Args)) - - // Check Flags - validatorFlag := queryValidatorSlashes.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - startHeightFlag := queryValidatorSlashes.Flag(flagStartHeight) - require.NotNil(t, startHeightFlag) - require.Equal(t, []string{"true"}, startHeightFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - endHeightFlag := queryValidatorSlashes.Flag(flagEndHeight) - require.NotNil(t, endHeightFlag) - require.Equal(t, []string{"true"}, endHeightFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryDelegatorRewards(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryDelegatroRewards := GetCmdQueryDelegatorRewards(dt.QuerierRoute, cdc) - - // Name check - require.Equal(t, dtk.QueryDelegationRewards, "delegation_"+queryDelegatroRewards.Name()) - require.Equal(t, dtk.QueryDelegatorTotalRewards, "delegator_total_"+queryDelegatroRewards.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryDelegatroRewards.Args)) - - // Check Flags - validatorFlag := queryDelegatroRewards.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Nil(t, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - delegatorFlag := queryDelegatroRewards.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} diff --git a/x/distribution/client/cli/flags.go b/x/distribution/client/cli/flags.go deleted file mode 100755 index 666d86315..000000000 --- a/x/distribution/client/cli/flags.go +++ /dev/null @@ -1,19 +0,0 @@ -package cli - -const ( - flagAddressValidator = "validator" - flagAddressDelegator = "delegator" - flagStartHeight = "start" - flagEndHeight = "end" - flagOnlyFromValidator = "only-from-validator" - flagIsValidator = "is-validator" - flagComission = "commission" - flagWithdrawTo = "withdraw-to" - flagOffline = "offline" - flagMaxMessagesPerTx = "max-msgs" -) - -const ( - // MaxMessagesPerTxDefault is max # of msg to prevent tx ledger fails due to memory constraint - MaxMessagesPerTxDefault = 5 -) diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go deleted file mode 100755 index fb3192470..000000000 --- a/x/distribution/client/cli/query.go +++ /dev/null @@ -1,215 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - - "github.com/terra-project/core/x/distribution/client/common" -) - -// GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "params", - Args: cobra.NoArgs, - Short: "Query distribution params", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - params, err := common.QueryParams(cliCtx, queryRoute) - if err != nil { - return err - } - return cliCtx.PrintOutput(params) - }, - } -} - -// GetCmdQueryValidatorOutstandingRewards implements the query validator outstanding rewards command. -func GetCmdQueryValidatorOutstandingRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "validator-outstanding-rewards --validator [validator-address]", - Args: cobra.NoArgs, - Short: "Query distribution validator outstanding (un-withdrawn) rewards", - Long: strings.TrimSpace(`Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations: - -$ terracli query dist validator-outstanding-rewards --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - bz := cdc.MustMarshalJSON(distr.NewQueryValidatorOutstandingRewardsParams(valAddr)) - - route := fmt.Sprintf("custom/%s/validator_outstanding_rewards", queryRoute) - res, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err - } - - var outstandingRewards types.ValidatorOutstandingRewards - cdc.MustUnmarshalJSON(res, &outstandingRewards) - return cliCtx.PrintOutput(outstandingRewards) - }, - } - - cmd.Flags().String(flagAddressValidator, "", "The Bech32 address of the validator") - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryValidatorCommission implements the query validator commission command. -func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "validator-commission --validator [validator]", - Args: cobra.NoArgs, - Short: "Query distribution validator commission", - Long: strings.TrimSpace(`Query validator commission rewards from delegators to a validator: - -$ terracli query distr commission --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - res, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, valAddr) - if err != nil { - return err - } - - var valCom types.ValidatorAccumulatedCommission - cdc.MustUnmarshalJSON(res, &valCom) - return cliCtx.PrintOutput(valCom) - }, - } - - cmd.Flags().String(flagAddressValidator, "", "The Bech32 address of the validator") - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryValidatorSlashes implements the query validator slashes command. -func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "validator-slashes --validator [validator] --start [start-height] --end [end-height]", - Args: cobra.NoArgs, - Short: "Query distribution validator slashes", - Long: strings.TrimSpace(`Query all slashes of a validator for a given block range: - -$ terracli query distr slashes --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --start 0 --end 100 -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - startHeightStr := viper.GetString(flagStartHeight) - startHeight, err := strconv.ParseUint(startHeightStr, 10, 64) - if err != nil { - return fmt.Errorf("start-height %s not a valid uint, please input a valid start-height", startHeightStr) - } - - endHeightStr := viper.GetString(flagEndHeight) - endHeight, err := strconv.ParseUint(endHeightStr, 10, 64) - if err != nil { - return fmt.Errorf("end-height %s not a valid uint, please input a valid end-height", endHeightStr) - } - - params := distr.NewQueryValidatorSlashesParams(valAddr, startHeight, endHeight) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_slashes", queryRoute), bz) - if err != nil { - return err - } - - var slashes types.ValidatorSlashEvents - cdc.MustUnmarshalJSON(res, &slashes) - return cliCtx.PrintOutput(slashes) - }, - } - - cmd.Flags().String(flagAddressValidator, "", "The Bech32 address of the validator") - cmd.Flags().String(flagStartHeight, "", "The start height of given query") - cmd.Flags().String(flagEndHeight, "", "The end height of given query") - - cmd.MarkFlagRequired(flagAddressValidator) - cmd.MarkFlagRequired(flagStartHeight) - cmd.MarkFlagRequired(flagEndHeight) - - return cmd -} - -// GetCmdQueryDelegatorRewards implements the query delegator rewards command. -func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "rewards --delegator [delegator-addr] --validator [validator-addr]", - Args: cobra.NoArgs, - Short: "Query all distribution delegator rewards or rewards from a particular validator", - Long: strings.TrimSpace(`Query all rewards earned by a delegator, optionally restrict to rewards from a single validator: - -$ terracli query distr rewards --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -$ terracli query distr rewards --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - var resp []byte - var err error - - delAddrStr := viper.GetString(flagAddressDelegator) - valAddrStr := viper.GetString(flagAddressValidator) - - if len(valAddrStr) == 0 { - resp, err = common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, delAddrStr) - } else { - resp, err = common.QueryDelegationRewards(cliCtx, cdc, queryRoute, delAddrStr, valAddrStr) - } - - if err != nil { - return err - } - - var result sdk.DecCoins - cdc.MustUnmarshalJSON(resp, &result) - return cliCtx.PrintOutput(result) - }, - } - - cmd.Flags().String(flagAddressValidator, "", "The Bech32 address of the validator") - cmd.Flags().String(flagAddressDelegator, "", "The Bech32 address of the delegator") - - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go deleted file mode 100755 index c10af431a..000000000 --- a/x/distribution/client/cli/tx.go +++ /dev/null @@ -1,176 +0,0 @@ -package cli - -import ( - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - - "github.com/terra-project/core/x/distribution/client/common" -) - -type generateOrBroadcastFunc func(context.CLIContext, authtxb.TxBuilder, []sdk.Msg, bool) error - -func splitAndApply( - generateOrBroadcast generateOrBroadcastFunc, - cliCtx context.CLIContext, - txBldr authtxb.TxBuilder, - msgs []sdk.Msg, - chunkSize int, - offline bool, -) error { - - if chunkSize == 0 { - return generateOrBroadcast(cliCtx, txBldr, msgs, offline) - } - - // split messages into slices of length chunkSize - totalMessages := len(msgs) - for i := 0; i < len(msgs); i += chunkSize { - - sliceEnd := i + chunkSize - if sliceEnd > totalMessages { - sliceEnd = totalMessages - } - - msgChunk := msgs[i:sliceEnd] - if err := generateOrBroadcast(cliCtx, txBldr, msgChunk, offline); err != nil { - return err - } - } - - return nil -} - -// command to withdraw rewards -func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "withdraw-rewards --validator [validator-addr]", - Args: cobra.NoArgs, - Short: "withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", - Long: strings.TrimSpace(`withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator: - -$ terracli tx distr withdraw-rewards --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldcl4phj --from mykey -$ terracli tx distr withdraw-rewards --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldcl4phj --from mykey --commission -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - delAddr := cliCtx.GetFromAddress() - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} - if viper.GetBool(flagComission) { - msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) - } - - offline := viper.GetBool(flagOffline) - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - } - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs, offline) - }, - } - - cmd.Flags().String(flagAddressValidator, "", "The Bech32 address of the validator") - cmd.Flags().Bool(flagComission, false, "also withdraw validator's commission") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - cmd.MarkFlagRequired(flagAddressValidator) - return cmd -} - -// command to withdraw all rewards -func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command { - cmd := &cobra.Command{ - Use: "withdraw-all-rewards", - Args: cobra.NoArgs, - Short: "withdraw all delegations rewards for a delegator", - Long: strings.TrimSpace(`Withdraw all rewards for a single delegator: - -$ terracli tx distr withdraw-all-rewards --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - delAddr := cliCtx.GetFromAddress() - msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) - if err != nil { - return err - } - - chunkSize := viper.GetInt(flagMaxMessagesPerTx) - return splitAndApply(utils.GenerateOrBroadcastMsgs, cliCtx, txBldr, msgs, chunkSize, false) - }, - } - - cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") - return cmd -} - -// command to replace a delegator's withdrawal address -func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "set-withdraw-addr --withdraw-to [withdraw-addr]", - Args: cobra.NoArgs, - Short: "change the default withdraw address for rewards associated with an address", - Long: strings.TrimSpace(`Set the withdraw address for rewards associated with a delegator address: - -$ terracli tx set-withdraw-addr --withdraw-to terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - delAddr := cliCtx.GetFromAddress() - - withdrawAddrToStr := viper.GetString(flagWithdrawTo) - withdrawAddrTo, err := sdk.AccAddressFromBech32(withdrawAddrToStr) - if err != nil { - return err - } - - offline := viper.GetBool(flagOffline) - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - } - - msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddrTo) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().String(flagWithdrawTo, "", "Target address to withdraw") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - cmd.MarkFlagRequired(flagWithdrawTo) - - return cmd -} diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go deleted file mode 100755 index daeab0499..000000000 --- a/x/distribution/client/common/common.go +++ /dev/null @@ -1,139 +0,0 @@ -package common - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" -) - -// QueryParams actually queries distribution params. -func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { - - route := fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) - retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) - retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) - retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return PrettyParams{}, err - } - - return NewPrettyParams(retBaseProposerReward, - retBonusProposerReward, retWithdrawAddrEnabled), nil -} - -// QueryDelegatorTotalRewards queries delegator total rewards. -func QueryDelegatorTotalRewards(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr string) ([]byte, error) { - - delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) - if err != nil { - return nil, err - } - - return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute), - cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), - ) -} - -// QueryDelegationRewards queries a delegation rewards. -func QueryDelegationRewards(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr, valAddr string) ([]byte, error) { - - delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) - if err != nil { - return nil, err - } - validatorAddr, err := sdk.ValAddressFromBech32(valAddr) - if err != nil { - return nil, err - } - - return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegation_rewards", queryRoute), - cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), - ) -} - -// QueryDelegatorValidators returns delegator's list of validators -// it submitted delegations to. -func QueryDelegatorValidators(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { - - return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/delegator_validators", queryRoute), - cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), - ) -} - -// QueryValidatorCommission returns a validator's commission. -func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { - - return cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/validator_commission", queryRoute), - cdc.MustMarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)), - ) -} - -// WithdrawAllDelegatorRewards builds a multi-message slice to be used -// to withdraw all delegations rewards for the given delegator. -func WithdrawAllDelegatorRewards(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { - - // retrieve the comprehensive list of all validators which the - // delegator had submitted delegations to - bz, err := QueryDelegatorValidators(cliCtx, cdc, queryRoute, delegatorAddr) - if err != nil { - return nil, err - } - - var validators []sdk.ValAddress - if err := cdc.UnmarshalJSON(bz, &validators); err != nil { - return nil, err - } - - // build multi-message transaction - var msgs []sdk.Msg - for _, valAddr := range validators { - msg := distr.NewMsgWithdrawDelegatorReward(delegatorAddr, valAddr) - if err := msg.ValidateBasic(); err != nil { - return nil, err - } - msgs = append(msgs, msg) - } - - return msgs, nil -} - -// WithdrawValidatorRewardsAndCommission builds a two-message message slice to be -// used to withdraw both validation's commission and self-delegation reward. -func WithdrawValidatorRewardsAndCommission(validatorAddr sdk.ValAddress) ([]sdk.Msg, error) { - - commissionMsg := distr.NewMsgWithdrawValidatorCommission(validatorAddr) - if err := commissionMsg.ValidateBasic(); err != nil { - return nil, err - } - - // build and validate MsgWithdrawDelegatorReward - rewardMsg := distr.NewMsgWithdrawDelegatorReward( - sdk.AccAddress(validatorAddr.Bytes()), validatorAddr) - if err := rewardMsg.ValidateBasic(); err != nil { - return nil, err - } - - return []sdk.Msg{commissionMsg, rewardMsg}, nil -} diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go deleted file mode 100755 index ff9cbfd75..000000000 --- a/x/distribution/client/common/common_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package common - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" -) - -func TestQueryDelegationRewardsAddrValidation(t *testing.T) { - cdc := codec.New() - ctx := context.NewCLIContext().WithCodec(cdc) - type args struct { - delAddr string - valAddr string - } - tests := []struct { - name string - args args - want []byte - wantErr bool - }{ - {"invalid delegator address", args{"invalid", ""}, nil, true}, - {"empty delegator address", args{"", ""}, nil, true}, - {"invalid validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", "invalid"}, nil, true}, - {"empty validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", ""}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := QueryDelegationRewards(ctx, cdc, "", tt.args.delAddr, tt.args.valAddr) - require.True(t, err != nil, tt.wantErr) - }) - } -} diff --git a/x/distribution/client/common/pretty_params.go b/x/distribution/client/common/pretty_params.go deleted file mode 100755 index 53c0e6370..000000000 --- a/x/distribution/client/common/pretty_params.go +++ /dev/null @@ -1,31 +0,0 @@ -package common - -import ( - "encoding/json" - "fmt" -) - -// Convenience struct for CLI output -type PrettyParams struct { - BaseProposerReward json.RawMessage `json:"base_proposer_reward"` - BonusProposerReward json.RawMessage `json:"bonus_proposer_reward"` - WithdrawAddrEnabled json.RawMessage `json:"withdraw_addr_enabled"` -} - -// Construct a new PrettyParams -func NewPrettyParams(baseProposerReward json.RawMessage, bonusProposerReward json.RawMessage, withdrawAddrEnabled json.RawMessage) PrettyParams { - return PrettyParams{ - BaseProposerReward: baseProposerReward, - BonusProposerReward: bonusProposerReward, - WithdrawAddrEnabled: withdrawAddrEnabled, - } -} - -func (pp PrettyParams) String() string { - return fmt.Sprintf(`Distribution Params: - Base Proposer Reward: %s - Bonus Proposer Reward: %s - Withdraw Addr Enabled: %s`, - pp.BaseProposerReward, pp.BonusProposerReward, pp.WithdrawAddrEnabled) - -} diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go deleted file mode 100755 index bcd92e6ac..000000000 --- a/x/distribution/client/module_client.go +++ /dev/null @@ -1,53 +0,0 @@ -package client - -import ( - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" - - "github.com/cosmos/cosmos-sdk/client" - distCmds "github.com/terra-project/core/x/distribution/client/cli" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - distQueryCmd := &cobra.Command{ - Use: "distr", - Short: "Querying commands for the distribution module", - } - - distQueryCmd.AddCommand(client.GetCommands( - distCmds.GetCmdQueryParams(mc.storeKey, mc.cdc), - distCmds.GetCmdQueryValidatorOutstandingRewards(mc.storeKey, mc.cdc), - distCmds.GetCmdQueryValidatorCommission(mc.storeKey, mc.cdc), - distCmds.GetCmdQueryValidatorSlashes(mc.storeKey, mc.cdc), - distCmds.GetCmdQueryDelegatorRewards(mc.storeKey, mc.cdc), - )...) - - return distQueryCmd -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - distTxCmd := &cobra.Command{ - Use: "distr", - Short: "Distribution transactions subcommands", - } - - distTxCmd.AddCommand(client.PostCommands( - distCmds.GetCmdWithdrawRewards(mc.cdc), - distCmds.GetCmdSetWithdrawAddr(mc.cdc), - distCmds.GetCmdWithdrawAllRewards(mc.cdc, mc.storeKey), - )...) - - return distTxCmd -} diff --git a/x/distribution/client/module_client_test.go b/x/distribution/client/module_client_test.go deleted file mode 100644 index e5f8c9aeb..000000000 --- a/x/distribution/client/module_client_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "params": true, - "validator-outstanding-rewards": true, - "validator-commission": true, - "validator-slashes": true, - "rewards": true, - } - - txCmdList = map[string]bool{ - "withdraw-rewards": true, - "set-withdraw-addr": true, - "withdraw-all-rewards": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go deleted file mode 100755 index 053cb8ddf..000000000 --- a/x/distribution/client/rest/query.go +++ /dev/null @@ -1,253 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - - "github.com/terra-project/core/x/distribution/client/common" -) - -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, - cdc *codec.Codec, queryRoute string) { - - // Get the total rewards balance from all delegations - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/rewards", - delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Query a delegation reward - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - delegationRewardsHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Get the rewards withdrawal address - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/withdraw_address", - delegatorWithdrawalAddrHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Validator distribution information - r.HandleFunc( - "/distribution/validators/{validatorAddr}", - validatorInfoHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Commission and self-delegation rewards of a single a validator - r.HandleFunc( - "/distribution/validators/{validatorAddr}/rewards", - validatorRewardsHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Outstanding rewards of a single validator - r.HandleFunc( - "/distribution/validators/{validatorAddr}/outstanding_rewards", - outstandingRewardsHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - - // Get the current distribution parameter values - r.HandleFunc( - "/distribution/params", - paramsHandlerFn(cliCtx, cdc, queryRoute), - ).Methods("GET") - -} - -// HTTP request handler to query the total rewards balance from all delegations -func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - // query for rewards from a particular delegator - res, ok := checkResponseQueryDelegatorTotalRewards(w, cliCtx, cdc, queryRoute, - mux.Vars(r)["delegatorAddr"]) - if !ok { - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query a delegation rewards -func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - // query for rewards from a particular delegation - res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, - mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) - if !ok { - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query a delegation rewards -func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - delegatorAddr, ok := checkDelegatorAddressVar(w, r) - if !ok { - return - } - - bz := cdc.MustMarshalJSON(distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// ValidatorDistInfo defines the properties of -// validator distribution information response. -type ValidatorDistInfo struct { - OperatorAddress sdk.AccAddress `json:"operator_address"` - SelfBondRewards sdk.DecCoins `json:"self_bond_rewards"` - ValidatorCommission types.ValidatorAccumulatedCommission `json:"val_commission"` -} - -// NewValidatorDistInfo creates a new instance of ValidatorDistInfo. -func NewValidatorDistInfo(operatorAddr sdk.AccAddress, rewards sdk.DecCoins, - commission types.ValidatorAccumulatedCommission) ValidatorDistInfo { - return ValidatorDistInfo{ - OperatorAddress: operatorAddr, - SelfBondRewards: rewards, - ValidatorCommission: commission, - } -} - -// HTTP request handler to query validator's distribution information -func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - valAddr := mux.Vars(r)["validatorAddr"] - validatorAddr, ok := checkValidatorAddressVar(w, r) - if !ok { - return - } - - // query commission - commissionRes, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - var valCom types.ValidatorAccumulatedCommission - cdc.MustUnmarshalJSON(commissionRes, &valCom) - - // self bond rewards - delAddr := sdk.AccAddress(validatorAddr) - rewardsRes, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, - delAddr.String(), valAddr) - if !ok { - return - } - - var rewards sdk.DecCoins - cdc.MustUnmarshalJSON(rewardsRes, &rewards) - - // Prepare response - res := cdc.MustMarshalJSON(NewValidatorDistInfo(delAddr, rewards, valCom)) - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query validator's commission and self-delegation rewards -func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - valAddr := mux.Vars(r)["validatorAddr"] - validatorAddr, ok := checkValidatorAddressVar(w, r) - if !ok { - return - } - - delAddr := sdk.AccAddress(validatorAddr).String() - res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) - if !ok { - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query the distribution params values -func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - params, err := common.QueryParams(cliCtx, queryRoute) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, params, cliCtx.Indent) - } -} - -// HTTP request handler to query the outstanding rewards -func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute string) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - validatorAddr, ok := checkValidatorAddressVar(w, r) - if !ok { - return - } - - bin := cdc.MustMarshalJSON(distribution.NewQueryValidatorOutstandingRewardsParams(validatorAddr)) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_outstanding_rewards", queryRoute), bin) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func checkResponseQueryDelegatorTotalRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr string) (res []byte, ok bool) { - - res, err := common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, delAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return nil, false - } - - return res, true -} - -func checkResponseQueryDelegationRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, - queryRoute, delAddr, valAddr string) (res []byte, ok bool) { - - res, err := common.QueryDelegationRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return nil, false - } - - return res, true -} diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go deleted file mode 100755 index 6596ccdf3..000000000 --- a/x/distribution/client/rest/rest.go +++ /dev/null @@ -1,14 +0,0 @@ -package rest - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" -) - -// RegisterRoutes register distribution REST routes. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { - registerQueryRoutes(cliCtx, r, cdc, queryRoute) - registerTxRoutes(cliCtx, r, cdc, queryRoute) -} diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go deleted file mode 100755 index e19905f10..000000000 --- a/x/distribution/client/rest/tx.go +++ /dev/null @@ -1,204 +0,0 @@ -package rest - -import ( - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - - "github.com/terra-project/core/x/distribution/client/common" -) - -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, - cdc *codec.Codec, queryRoute string) { - - // Withdraw all delegator rewards - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/rewards", - withdrawDelegatorRewardsHandlerFn(cdc, cliCtx, queryRoute), - ).Methods("POST") - - // Withdraw delegation rewards - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - withdrawDelegationRewardsHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // Replace the rewards withdrawal address - r.HandleFunc( - "/distribution/delegators/{delegatorAddr}/withdraw_address", - setDelegatorWithdrawalAddrHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // Withdraw validator rewards and commission - r.HandleFunc( - "/distribution/validators/{validatorAddr}/rewards", - withdrawValidatorRewardsHandlerFn(cdc, cliCtx), - ).Methods("POST") - -} - -type ( - withdrawRewardsReq struct { - BaseReq rest.BaseReq `json:"base_req"` - } - - setWithdrawalAddrReq struct { - BaseReq rest.BaseReq `json:"base_req"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address"` - } -) - -// Withdraw delegator rewards -func withdrawDelegatorRewardsHandlerFn( - cdc *codec.Codec, cliCtx context.CLIContext, queryRoute string, -) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - var req withdrawRewardsReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - // read and validate URL's variables - delAddr, ok := checkDelegatorAddressVar(w, r) - if !ok { - return - } - - msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) - } -} - -// Withdraw delegation rewards -func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req withdrawRewardsReq - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - // read and validate URL's variables - delAddr, ok := checkDelegatorAddressVar(w, r) - if !ok { - return - } - - valAddr, ok := checkValidatorAddressVar(w, r) - if !ok { - return - } - - msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -// Replace the rewards withdrawal address -func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req setWithdrawalAddrReq - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - // read and validate URL's variables - delAddr, ok := checkDelegatorAddressVar(w, r) - if !ok { - return - } - - msg := types.NewMsgSetWithdrawAddress(delAddr, req.WithdrawAddress) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -// Withdraw validator rewards and commission -func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req withdrawRewardsReq - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - // read and validate URL's variable - valAddr, ok := checkValidatorAddressVar(w, r) - if !ok { - return - } - - // prepare multi-message transaction - msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) - } -} - -// Auxiliary - -func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { - addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return nil, false - } - return addr, true -} - -func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { - addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return nil, false - } - return addr, true -} diff --git a/x/distribution/codec.go b/x/distribution/codec.go deleted file mode 100644 index 39f565ac3..000000000 --- a/x/distribution/codec.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - - distr "github.com/cosmos/cosmos-sdk/x/distribution" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(distr.MsgWithdrawDelegatorReward{}, "distribution/MsgWithdrawDelegationReward", nil) - cdc.RegisterConcrete(distr.MsgWithdrawValidatorCommission{}, "distribution/MsgWithdrawValidatorCommission", nil) - cdc.RegisterConcrete(distr.MsgSetWithdrawAddress{}, "distribution/MsgModifyWithdrawAddress", nil) -} - -// generic sealed codec to be used throughout module -var MsgCdc *codec.Codec - -func init() { - cdc := codec.New() - RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - MsgCdc = cdc.Seal() -} diff --git a/x/distribution/cosmos_alias.go b/x/distribution/cosmos_alias.go new file mode 100644 index 000000000..5d83d1cca --- /dev/null +++ b/x/distribution/cosmos_alias.go @@ -0,0 +1,154 @@ +// nolint +package distribution + +import ( + distr "github.com/cosmos/cosmos-sdk/x/distribution" +) + +const ( + DefaultParamspace = distr.DefaultParamspace + DefaultCodespace = distr.DefaultCodespace + CodeInvalidInput = distr.CodeInvalidInput + CodeNoDistributionInfo = distr.CodeNoDistributionInfo + CodeNoValidatorCommission = distr.CodeNoValidatorCommission + CodeSetWithdrawAddrDisabled = distr.CodeSetWithdrawAddrDisabled + ModuleName = distr.ModuleName + StoreKey = distr.StoreKey + RouterKey = distr.RouterKey + QuerierRoute = distr.QuerierRoute + ProposalTypeCommunityPoolSpend = distr.ProposalTypeCommunityPoolSpend + QueryParams = distr.QueryParams + QueryValidatorOutstandingRewards = distr.QueryValidatorOutstandingRewards + QueryValidatorCommission = distr.QueryValidatorCommission + QueryValidatorSlashes = distr.QueryValidatorSlashes + QueryDelegationRewards = distr.QueryDelegationRewards + QueryDelegatorTotalRewards = distr.QueryDelegatorTotalRewards + QueryDelegatorValidators = distr.QueryDelegatorValidators + QueryWithdrawAddr = distr.QueryWithdrawAddr + QueryCommunityPool = distr.QueryCommunityPool + ParamCommunityTax = distr.ParamCommunityTax + ParamBaseProposerReward = distr.ParamBaseProposerReward + ParamBonusProposerReward = distr.ParamBonusProposerReward + ParamWithdrawAddrEnabled = distr.ParamWithdrawAddrEnabled +) + +var ( + // functions aliases + RegisterInvariants = distr.RegisterInvariants + AllInvariants = distr.AllInvariants + NonNegativeOutstandingInvariant = distr.NonNegativeOutstandingInvariant + CanWithdrawInvariant = distr.CanWithdrawInvariant + ReferenceCountInvariant = distr.ReferenceCountInvariant + ModuleAccountInvariant = distr.ModuleAccountInvariant + NewKeeper = distr.NewKeeper + GetValidatorOutstandingRewardsAddress = distr.GetValidatorOutstandingRewardsAddress + GetDelegatorWithdrawInfoAddress = distr.GetDelegatorWithdrawInfoAddress + GetDelegatorStartingInfoAddresses = distr.GetDelegatorStartingInfoAddresses + GetValidatorHistoricalRewardsAddressPeriod = distr.GetValidatorHistoricalRewardsAddressPeriod + GetValidatorCurrentRewardsAddress = distr.GetValidatorCurrentRewardsAddress + GetValidatorAccumulatedCommissionAddress = distr.GetValidatorAccumulatedCommissionAddress + GetValidatorSlashEventAddressHeight = distr.GetValidatorSlashEventAddressHeight + GetValidatorOutstandingRewardsKey = distr.GetValidatorOutstandingRewardsKey + GetDelegatorWithdrawAddrKey = distr.GetDelegatorWithdrawAddrKey + GetDelegatorStartingInfoKey = distr.GetDelegatorStartingInfoKey + GetValidatorHistoricalRewardsPrefix = distr.GetValidatorHistoricalRewardsPrefix + GetValidatorHistoricalRewardsKey = distr.GetValidatorHistoricalRewardsKey + GetValidatorCurrentRewardsKey = distr.GetValidatorCurrentRewardsKey + GetValidatorAccumulatedCommissionKey = distr.GetValidatorAccumulatedCommissionKey + GetValidatorSlashEventPrefix = distr.GetValidatorSlashEventPrefix + GetValidatorSlashEventKey = distr.GetValidatorSlashEventKey + GetValidatorSlashEventKeyPrefix = distr.GetValidatorSlashEventKeyPrefix + ParamKeyTable = distr.ParamKeyTable + HandleCommunityPoolSpendProposal = distr.HandleCommunityPoolSpendProposal + NewQuerier = distr.NewQuerier + MakeTestCodec = distr.MakeTestCodec + CreateTestInputDefault = distr.CreateTestInputDefault + CreateTestInputAdvanced = distr.CreateTestInputAdvanced + NewDelegatorStartingInfo = distr.NewDelegatorStartingInfo + ErrNilDelegatorAddr = distr.ErrNilDelegatorAddr + ErrNilWithdrawAddr = distr.ErrNilWithdrawAddr + ErrNilValidatorAddr = distr.ErrNilValidatorAddr + ErrNoDelegationDistInfo = distr.ErrNoDelegationDistInfo + ErrNoValidatorDistInfo = distr.ErrNoValidatorDistInfo + ErrNoValidatorCommission = distr.ErrNoValidatorCommission + ErrSetWithdrawAddrDisabled = distr.ErrSetWithdrawAddrDisabled + ErrBadDistribution = distr.ErrBadDistribution + ErrInvalidProposalAmount = distr.ErrInvalidProposalAmount + ErrEmptyProposalRecipient = distr.ErrEmptyProposalRecipient + InitialFeePool = distr.InitialFeePool + NewMsgSetWithdrawAddress = distr.NewMsgSetWithdrawAddress + NewMsgWithdrawDelegatorReward = distr.NewMsgWithdrawDelegatorReward + NewMsgWithdrawValidatorCommission = distr.NewMsgWithdrawValidatorCommission + NewCommunityPoolSpendProposal = distr.NewCommunityPoolSpendProposal + NewQueryValidatorOutstandingRewardsParams = distr.NewQueryValidatorOutstandingRewardsParams + NewQueryValidatorCommissionParams = distr.NewQueryValidatorCommissionParams + NewQueryValidatorSlashesParams = distr.NewQueryValidatorSlashesParams + NewQueryDelegationRewardsParams = distr.NewQueryDelegationRewardsParams + NewQueryDelegatorParams = distr.NewQueryDelegatorParams + NewQueryDelegatorWithdrawAddrParams = distr.NewQueryDelegatorWithdrawAddrParams + NewQueryDelegatorTotalRewardsResponse = distr.NewQueryDelegatorTotalRewardsResponse + NewDelegationDelegatorReward = distr.NewDelegationDelegatorReward + NewValidatorHistoricalRewards = distr.NewValidatorHistoricalRewards + NewValidatorCurrentRewards = distr.NewValidatorCurrentRewards + InitialValidatorAccumulatedCommission = distr.InitialValidatorAccumulatedCommission + NewValidatorSlashEvent = distr.NewValidatorSlashEvent + NewCommunityPoolSpendProposalHandler = distr.NewCommunityPoolSpendProposalHandler + NewCosmosAppModule = distr.NewAppModule + + // variable aliases + FeePoolKey = distr.FeePoolKey + ProposerKey = distr.ProposerKey + ValidatorOutstandingRewardsPrefix = distr.ValidatorOutstandingRewardsPrefix + DelegatorWithdrawAddrPrefix = distr.DelegatorWithdrawAddrPrefix + DelegatorStartingInfoPrefix = distr.DelegatorStartingInfoPrefix + ValidatorHistoricalRewardsPrefix = distr.ValidatorHistoricalRewardsPrefix + ValidatorCurrentRewardsPrefix = distr.ValidatorCurrentRewardsPrefix + ValidatorAccumulatedCommissionPrefix = distr.ValidatorAccumulatedCommissionPrefix + ValidatorSlashEventPrefix = distr.ValidatorSlashEventPrefix + ParamStoreKeyCommunityTax = distr.ParamStoreKeyCommunityTax + ParamStoreKeyBaseProposerReward = distr.ParamStoreKeyBaseProposerReward + ParamStoreKeyBonusProposerReward = distr.ParamStoreKeyBonusProposerReward + ParamStoreKeyWithdrawAddrEnabled = distr.ParamStoreKeyWithdrawAddrEnabled + TestAddrs = distr.TestAddrs + EventTypeRewards = distr.EventTypeRewards + EventTypeCommission = distr.EventTypeCommission + AttributeValueCategory = distr.AttributeValueCategory + AttributeKeyValidator = distr.AttributeKeyValidator + CosmosModuleCdc = distr.ModuleCdc +) + +type ( + Hooks = distr.Hooks + Keeper = distr.Keeper + DelegatorStartingInfo = distr.DelegatorStartingInfo + CodeType = distr.CodeType + FeePool = distr.FeePool + DelegatorWithdrawInfo = distr.DelegatorWithdrawInfo + ValidatorOutstandingRewardsRecord = distr.ValidatorOutstandingRewardsRecord + ValidatorAccumulatedCommissionRecord = distr.ValidatorAccumulatedCommissionRecord + ValidatorHistoricalRewardsRecord = distr.ValidatorHistoricalRewardsRecord + ValidatorCurrentRewardsRecord = distr.ValidatorCurrentRewardsRecord + DelegatorStartingInfoRecord = distr.DelegatorStartingInfoRecord + ValidatorSlashEventRecord = distr.ValidatorSlashEventRecord + GenesisState = distr.GenesisState + MsgSetWithdrawAddress = distr.MsgSetWithdrawAddress + MsgWithdrawDelegatorReward = distr.MsgWithdrawDelegatorReward + MsgWithdrawValidatorCommission = distr.MsgWithdrawValidatorCommission + CommunityPoolSpendProposal = distr.CommunityPoolSpendProposal + QueryValidatorOutstandingRewardsParams = distr.QueryValidatorOutstandingRewardsParams + QueryValidatorCommissionParams = distr.QueryValidatorCommissionParams + QueryValidatorSlashesParams = distr.QueryValidatorSlashesParams + QueryDelegationRewardsParams = distr.QueryDelegationRewardsParams + QueryDelegatorParams = distr.QueryDelegatorParams + QueryDelegatorWithdrawAddrParams = distr.QueryDelegatorWithdrawAddrParams + QueryDelegatorTotalRewardsResponse = distr.QueryDelegatorTotalRewardsResponse + DelegationDelegatorReward = distr.DelegationDelegatorReward + ValidatorHistoricalRewards = distr.ValidatorHistoricalRewards + ValidatorCurrentRewards = distr.ValidatorCurrentRewards + ValidatorAccumulatedCommission = distr.ValidatorAccumulatedCommission + ValidatorSlashEvent = distr.ValidatorSlashEvent + ValidatorSlashEvents = distr.ValidatorSlashEvents + ValidatorOutstandingRewards = distr.ValidatorOutstandingRewards + CosmosAppModule = distr.AppModule + CosmosAppModuleBasic = distr.AppModuleBasic +) diff --git a/x/distribution/internal/types/codec.go b/x/distribution/internal/types/codec.go new file mode 100644 index 000000000..e8fa2d37b --- /dev/null +++ b/x/distribution/internal/types/codec.go @@ -0,0 +1,23 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/distribution" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(distribution.MsgWithdrawDelegatorReward{}, "distribution/MsgWithdrawDelegationReward", nil) + cdc.RegisterConcrete(distribution.MsgWithdrawValidatorCommission{}, "distribution/MsgWithdrawValidatorCommission", nil) + cdc.RegisterConcrete(distribution.MsgSetWithdrawAddress{}, "distribution/MsgModifyWithdrawAddress", nil) +} + +// generic sealed codec to be used throughout module +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/distribution/module.go b/x/distribution/module.go new file mode 100644 index 000000000..3742f1cef --- /dev/null +++ b/x/distribution/module.go @@ -0,0 +1,122 @@ +package distribution + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +// app module +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper, supplyKeeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/genaccounts/client/cli/genesis_accts.go b/x/genaccounts/client/cli/genesis_accts.go new file mode 100644 index 000000000..5ee89f38d --- /dev/null +++ b/x/genaccounts/client/cli/genesis_accts.go @@ -0,0 +1,108 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/genaccounts" + "github.com/terra-project/core/x/genutil" +) + +const ( + flagClientHome = "home-client" + flagVestingStart = "vesting-start-time" + flagVestingEnd = "vesting-end-time" + flagVestingAmt = "vesting-amount" +) + +// AddGenesisAccountCmd returns add-genesis-account cobra Command. +func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec, + defaultNodeHome, defaultClientHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", + Short: "Add genesis account to genesis.json", + Args: cobra.ExactArgs(2), + RunE: func(_ *cobra.Command, args []string) error { + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome)) + if err != nil { + return err + } + + info, err := kb.Get(args[0]) + if err != nil { + return err + } + + addr = info.GetAddress() + } + + coins, err := sdk.ParseCoins(args[1]) + if err != nil { + return err + } + + vestingStart := viper.GetInt64(flagVestingStart) + vestingEnd := viper.GetInt64(flagVestingEnd) + vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt)) + if err != nil { + return err + } + + genAcc := genaccounts.NewGenesisAccountRaw(addr, coins, vestingAmt, vestingStart, vestingEnd, nil, "", "") + if err := genAcc.Validate(); err != nil { + return err + } + + // retrieve the app state + genFile := config.GenesisFile() + appState, genDoc, err := genutil.GenesisStateFromGenFile(cdc, genFile) + if err != nil { + return err + } + + // add genesis account to the app state + var genesisAccounts genaccounts.GenesisAccounts + + cdc.MustUnmarshalJSON(appState[genaccounts.ModuleName], &genesisAccounts) + + if genesisAccounts.Contains(addr) { + return fmt.Errorf("cannot add account at existing address %v", addr) + } + + genesisAccounts = append(genesisAccounts, genAcc) + + genesisStateBz := cdc.MustMarshalJSON(genaccounts.GenesisState(genesisAccounts)) + appState[genaccounts.ModuleName] = genesisStateBz + + appStateJSON, err := cdc.MarshalJSON(appState) + if err != nil { + return err + } + + // export app state + genDoc.AppState = appStateJSON + + return genutil.ExportGenesisFile(genDoc, genFile) + }, + } + + cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") + cmd.Flags().String(flagClientHome, defaultClientHome, "client's home directory") + cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") + cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + return cmd +} diff --git a/x/genaccounts/codec.go b/x/genaccounts/codec.go new file mode 100644 index 000000000..70edd800e --- /dev/null +++ b/x/genaccounts/codec.go @@ -0,0 +1,14 @@ +package genaccounts + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// generic sealed codec to be used throughout this module +var moduleCdc *codec.Codec + +func init() { + moduleCdc = codec.New() + codec.RegisterCrypto(moduleCdc) + moduleCdc.Seal() +} diff --git a/x/genaccounts/doc.go b/x/genaccounts/doc.go new file mode 100644 index 000000000..60c6e3c73 --- /dev/null +++ b/x/genaccounts/doc.go @@ -0,0 +1,9 @@ +/* +Package genaccounts contains specialized functionality for initializing +accounts from genesis including: + - genesis account validation, + - initchain processing of genesis accounts, + - export processing (to genesis) of accounts, + - server command for adding accounts to the genesis file. +*/ +package genaccounts diff --git a/x/genaccounts/expected.go b/x/genaccounts/expected.go new file mode 100644 index 000000000..2f38b62e3 --- /dev/null +++ b/x/genaccounts/expected.go @@ -0,0 +1,13 @@ +package genaccounts + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/exported" +) + +// expected account keeper +type AccountKeeper interface { + NewAccount(sdk.Context, exported.Account) exported.Account + SetAccount(sdk.Context, exported.Account) + IterateAccounts(ctx sdk.Context, process func(exported.Account) (stop bool)) +} diff --git a/x/genaccounts/export.go b/x/genaccounts/export.go new file mode 100644 index 000000000..767cc8e17 --- /dev/null +++ b/x/genaccounts/export.go @@ -0,0 +1,25 @@ +package genaccounts + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/exported" +) + +// export genesis for all accounts +func ExportGenesis(ctx sdk.Context, accountKeeper AccountKeeper) GenesisState { + + // iterate to get the accounts + accounts := []GenesisAccount{} + accountKeeper.IterateAccounts(ctx, + func(acc exported.Account) (stop bool) { + account, err := NewGenesisAccountI(acc) + if err != nil { + panic(err) + } + accounts = append(accounts, account) + return false + }, + ) + + return accounts +} diff --git a/x/genaccounts/genesis_account.go b/x/genaccounts/genesis_account.go new file mode 100644 index 000000000..85eaaafa2 --- /dev/null +++ b/x/genaccounts/genesis_account.go @@ -0,0 +1,169 @@ +package genaccounts + +import ( + "errors" + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/auth" + "github.com/terra-project/core/x/supply" +) + +// GenesisAccount is a struct for account initialization used exclusively during genesis +type GenesisAccount struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + Sequence uint64 `json:"sequence_number" yaml:"sequence_number"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + + // vesting account fields + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation + StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time) + EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time) + + // for lazy vesting account + VestingSchedules []auth.VestingSchedule `json:"vesting_schedules" yaml:"vesting_schedules"` // lazy vesting schedules + + // module account fields + ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account + ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account +} + +// Validate checks for errors on the vesting and module account parameters +func (ga GenesisAccount) Validate() error { + if !ga.OriginalVesting.IsZero() { + if ga.OriginalVesting.IsAnyGT(ga.Coins) { + return errors.New("vesting amount cannot be greater than total amount") + } + + if len(ga.VestingSchedules) > 0 { // lazy vesting account + for _, schedule := range ga.VestingSchedules { + if !schedule.IsValid() { + return errors.New("invalid lazy vesting schedule") + } + } + } else if ga.StartTime >= ga.EndTime { // or normal vesting account + + return errors.New("vesting start-time cannot be before end-time") + } + } + + // don't allow blank (i.e just whitespaces) on the module name + if ga.ModuleName != "" && strings.TrimSpace(ga.ModuleName) == "" { + return errors.New("module account name cannot be blank") + } + + return nil +} + +// NewGenesisAccountRaw creates a new GenesisAccount object +func NewGenesisAccountRaw(address sdk.AccAddress, coins, + vestingAmount sdk.Coins, vestingStartTime, vestingEndTime int64, + lazyVestingSchedules []auth.VestingSchedule, module string, permissions ...string) GenesisAccount { + + return GenesisAccount{ + Address: address, + Coins: coins, + Sequence: 0, + AccountNumber: 0, // ignored set by the account keeper during InitGenesis + OriginalVesting: vestingAmount, + DelegatedFree: sdk.Coins{}, // ignored + DelegatedVesting: sdk.Coins{}, // ignored + StartTime: vestingStartTime, + EndTime: vestingEndTime, + VestingSchedules: lazyVestingSchedules, + ModuleName: module, + ModulePermissions: permissions, + } +} + +// NewGenesisAccount creates a GenesisAccount instance from a BaseAccount. +func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { + return GenesisAccount{ + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + } +} + +// NewGenesisAccountI creates a GenesisAccount instance from an Account interface. +func NewGenesisAccountI(acc auth.Account) (GenesisAccount, error) { + gacc := GenesisAccount{ + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + AccountNumber: acc.GetAccountNumber(), + Sequence: acc.GetSequence(), + } + + if err := gacc.Validate(); err != nil { + return gacc, err + } + + switch acc := acc.(type) { + case auth.LazyGradedVestingAccount: + gacc.OriginalVesting = acc.GetOriginalVesting() + gacc.DelegatedFree = acc.GetDelegatedFree() + gacc.DelegatedVesting = acc.GetDelegatedVesting() + gacc.VestingSchedules = acc.GetVestingSchedules() + case auth.VestingAccount: + gacc.OriginalVesting = acc.GetOriginalVesting() + gacc.DelegatedFree = acc.GetDelegatedFree() + gacc.DelegatedVesting = acc.GetDelegatedVesting() + gacc.StartTime = acc.GetStartTime() + gacc.EndTime = acc.GetEndTime() + case supply.ModuleAccountI: + gacc.ModuleName = acc.GetName() + gacc.ModulePermissions = acc.GetPermissions() + } + + return gacc, nil +} + +// ToAccount converts a GenesisAccount to an Account interface +func (ga *GenesisAccount) ToAccount() auth.Account { + bacc := auth.NewBaseAccount(ga.Address, ga.Coins.Sort(), nil, ga.AccountNumber, ga.Sequence) + + // vesting accounts + if !ga.OriginalVesting.IsZero() { + baseVestingAcc := auth.NewBaseVestingAccount( + bacc, ga.OriginalVesting, ga.DelegatedFree, + ga.DelegatedVesting, ga.EndTime, + ) + + switch { + case len(ga.VestingSchedules) != 0: + return auth.NewBaseLazyGradedVestingAccountRaw(baseVestingAcc, ga.VestingSchedules) + case ga.StartTime != 0 && ga.EndTime != 0: + return auth.NewContinuousVestingAccountRaw(baseVestingAcc, ga.StartTime) + case ga.EndTime != 0: + return auth.NewDelayedVestingAccountRaw(baseVestingAcc) + default: + panic(fmt.Sprintf("invalid genesis vesting account: %+v", ga)) + } + } + + // module accounts + if ga.ModuleName != "" { + return supply.NewModuleAccount(bacc, ga.ModuleName, ga.ModulePermissions...) + } + + return bacc +} + +//___________________________________ +type GenesisAccounts []GenesisAccount + +// genesis accounts contain an address +func (gaccs GenesisAccounts) Contains(acc sdk.AccAddress) bool { + for _, gacc := range gaccs { + if gacc.Address.Equals(acc) { + return true + } + } + return false +} diff --git a/x/genaccounts/genesis_account_test.go b/x/genaccounts/genesis_account_test.go new file mode 100644 index 000000000..7161fbd57 --- /dev/null +++ b/x/genaccounts/genesis_account_test.go @@ -0,0 +1,114 @@ +package genaccounts + +import ( + "errors" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/auth" + "github.com/terra-project/core/x/supply" +) + +func TestGenesisAccountValidate(t *testing.T) { + addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + tests := []struct { + name string + acc GenesisAccount + expErr error + }{ + { + "valid account", + NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, []auth.VestingSchedule{}, "", ""), + nil, + }, + { + "valid lazy vesting account", + NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("uluna", 100)), sdk.NewCoins(sdk.NewInt64Coin("uluna", 100)), 0, 0, []auth.VestingSchedule{auth.NewVestingSchedule("uluna", []auth.LazySchedule{auth.NewLazySchedule(0, 0, sdk.OneDec())})}, "", ""), + nil, + }, + { + "valid module account", + NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, []auth.VestingSchedule{}, "market", supply.Minter), + nil, + }, + { + "invalid vesting amount", + NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("uluna", 50)), + sdk.NewCoins(sdk.NewInt64Coin("uluna", 100)), 0, 0, []auth.VestingSchedule{}, "", ""), + errors.New("vesting amount cannot be greater than total amount"), + }, + { + "invalid vesting amount with multi coins", + NewGenesisAccountRaw(addr, + sdk.NewCoins(sdk.NewInt64Coin("uluna", 50), sdk.NewInt64Coin("eth", 50)), + sdk.NewCoins(sdk.NewInt64Coin("uluna", 100), sdk.NewInt64Coin("eth", 20)), + 0, 0, []auth.VestingSchedule{}, "", ""), + errors.New("vesting amount cannot be greater than total amount"), + }, + { + "invalid vesting times", + NewGenesisAccountRaw(addr, sdk.NewCoins(sdk.NewInt64Coin("uluna", 50)), + sdk.NewCoins(sdk.NewInt64Coin("uluna", 50)), 1654668078, 1554668078, []auth.VestingSchedule{}, "", ""), + errors.New("vesting start-time cannot be before end-time"), + }, + { + "invalid module account name", + NewGenesisAccountRaw(addr, sdk.NewCoins(), sdk.NewCoins(), 0, 0, []auth.VestingSchedule{}, " ", ""), + errors.New("module account name cannot be blank"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.acc.Validate() + require.Equal(t, tt.expErr, err) + }) + } +} + +func TestToAccount(t *testing.T) { + priv := ed25519.GenPrivKey() + addr := sdk.AccAddress(priv.PubKey().Address()) + + // base account + authAcc := auth.NewBaseAccountWithAddress(addr) + authAcc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 150))) + genAcc := NewGenesisAccount(&authAcc) + acc := genAcc.ToAccount() + require.IsType(t, &auth.BaseAccount{}, acc) + require.Equal(t, &authAcc, acc.(*auth.BaseAccount)) + + // vesting account + vacc := auth.NewContinuousVestingAccount( + &authAcc, time.Now().Unix(), time.Now().Add(24*time.Hour).Unix(), + ) + genAcc, err := NewGenesisAccountI(vacc) + require.NoError(t, err) + acc = genAcc.ToAccount() + require.IsType(t, &auth.ContinuousVestingAccount{}, acc) + require.Equal(t, vacc, acc.(*auth.ContinuousVestingAccount)) + + // lazy vesting account + lvacc := auth.NewBaseLazyGradedVestingAccount( + &authAcc, []auth.VestingSchedule{auth.NewVestingSchedule("uluna", []auth.LazySchedule{auth.NewLazySchedule(0, 0, sdk.OneDec())})}, + ) + genAcc, err = NewGenesisAccountI(lvacc) + require.NoError(t, err) + acc = genAcc.ToAccount() + require.IsType(t, &auth.BaseLazyGradedVestingAccount{}, acc) + require.Equal(t, lvacc, acc.(*auth.BaseLazyGradedVestingAccount)) + + // module account + macc := supply.NewEmptyModuleAccount("market", supply.Minter) + genAcc, err = NewGenesisAccountI(macc) + require.NoError(t, err) + acc = genAcc.ToAccount() + require.IsType(t, &supply.ModuleAccount{}, acc) + require.Equal(t, macc, acc.(*supply.ModuleAccount)) +} diff --git a/x/genaccounts/genesis_state.go b/x/genaccounts/genesis_state.go new file mode 100644 index 000000000..834ee5301 --- /dev/null +++ b/x/genaccounts/genesis_state.go @@ -0,0 +1,77 @@ +package genaccounts + +import ( + "encoding/json" + "fmt" + "sort" + "time" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// State to Unmarshal +type GenesisState GenesisAccounts + +// get the genesis state from the expected app state +func GetGenesisStateFromAppState(cdc *codec.Codec, appState map[string]json.RawMessage) GenesisState { + var genesisState GenesisState + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return genesisState +} + +// set the genesis state within the expected app state +func SetGenesisStateInAppState(cdc *codec.Codec, + appState map[string]json.RawMessage, genesisState GenesisState) map[string]json.RawMessage { + + genesisStateBz := cdc.MustMarshalJSON(genesisState) + appState[ModuleName] = genesisStateBz + return appState +} + +// Sanitize sorts accounts and coin sets. +func (gs GenesisState) Sanitize() { + sort.Slice(gs, func(i, j int) bool { + return gs[i].AccountNumber < gs[j].AccountNumber + }) + + for _, acc := range gs { + acc.Coins = acc.Coins.Sort() + } +} + +// ValidateGenesis performs validation of genesis accounts. It +// ensures that there are no duplicate accounts in the genesis state and any +// provided vesting accounts are valid. +func ValidateGenesis(genesisState GenesisState) error { + addrMap := make(map[string]bool, len(genesisState)) + for _, acc := range genesisState { + addrStr := acc.Address.String() + + // disallow any duplicate accounts + if _, ok := addrMap[addrStr]; ok { + return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) + } + + // validate any vesting fields + if !acc.OriginalVesting.IsZero() { + if acc.EndTime == 0 { + return fmt.Errorf("missing end time for vesting account; address: %s", addrStr) + } + + if acc.StartTime >= acc.EndTime { + return fmt.Errorf( + "vesting start time must before end time; address: %s, start: %s, end: %s", + addrStr, + time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339), + time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339), + ) + } + } + + addrMap[addrStr] = true + } + return nil +} diff --git a/x/genaccounts/genesis_state_test.go b/x/genaccounts/genesis_state_test.go new file mode 100644 index 000000000..7f50cd1c0 --- /dev/null +++ b/x/genaccounts/genesis_state_test.go @@ -0,0 +1,87 @@ +package genaccounts + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/auth" +) + +func TestSanitize(t *testing.T) { + + addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + authAcc1 := auth.NewBaseAccountWithAddress(addr1) + authAcc1.SetCoins(sdk.Coins{ + sdk.NewInt64Coin("bcoin", 150), + sdk.NewInt64Coin("acoin", 150), + }) + authAcc1.SetAccountNumber(1) + genAcc1 := NewGenesisAccount(&authAcc1) + + addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + authAcc2 := auth.NewBaseAccountWithAddress(addr2) + authAcc2.SetCoins(sdk.Coins{ + sdk.NewInt64Coin("acoin", 150), + sdk.NewInt64Coin("bcoin", 150), + }) + genAcc2 := NewGenesisAccount(&authAcc2) + + genesisState := GenesisState([]GenesisAccount{genAcc1, genAcc2}) + require.NoError(t, ValidateGenesis(genesisState)) + require.True(t, genesisState[0].AccountNumber > genesisState[1].AccountNumber) + require.Equal(t, genesisState[0].Coins[0].Denom, "bcoin") + require.Equal(t, genesisState[0].Coins[1].Denom, "acoin") + require.Equal(t, genesisState[1].Address, addr2) + genesisState.Sanitize() + require.False(t, genesisState[0].AccountNumber > genesisState[1].AccountNumber) + require.Equal(t, genesisState[1].Address, addr1) + require.Equal(t, genesisState[1].Coins[0].Denom, "acoin") + require.Equal(t, genesisState[1].Coins[1].Denom, "bcoin") +} + +var ( + pk1 = ed25519.GenPrivKey().PubKey() + pk2 = ed25519.GenPrivKey().PubKey() + addr1 = sdk.ValAddress(pk1.Address()) + addr2 = sdk.ValAddress(pk2.Address()) +) + +// require duplicate accounts fails validation +func TestValidateGenesisDuplicateAccounts(t *testing.T) { + acc1 := auth.NewBaseAccountWithAddress(sdk.AccAddress(addr1)) + acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 150)) + + genAccs := make([]GenesisAccount, 2) + genAccs[0] = NewGenesisAccount(&acc1) + genAccs[1] = NewGenesisAccount(&acc1) + + genesisState := GenesisState(genAccs) + err := ValidateGenesis(genesisState) + require.Error(t, err) +} + +// require invalid vesting account fails validation (invalid end time) +func TestValidateGenesisInvalidAccounts(t *testing.T) { + acc1 := auth.NewBaseAccountWithAddress(sdk.AccAddress(addr1)) + acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 150)) + acc2 := auth.NewBaseAccountWithAddress(sdk.AccAddress(addr2)) + acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 150)) + + genAccs := make([]GenesisAccount, 2) + genAccs[0] = NewGenesisAccount(&acc1) + genAccs[1] = NewGenesisAccount(&acc2) + + genesisState := GenesisState(genAccs) + genesisState[0].OriginalVesting = genesisState[0].Coins + err := ValidateGenesis(genesisState) + require.Error(t, err) + + genesisState[0].StartTime = 1548888000 + genesisState[0].EndTime = 1548775410 + err = ValidateGenesis(genesisState) + require.Error(t, err) +} diff --git a/x/genaccounts/init.go b/x/genaccounts/init.go new file mode 100644 index 000000000..e58beef1d --- /dev/null +++ b/x/genaccounts/init.go @@ -0,0 +1,18 @@ +package genaccounts + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// initialize accounts and deliver genesis transactions +func InitGenesis(ctx sdk.Context, _ *codec.Codec, accountKeeper AccountKeeper, genesisState GenesisState) { + genesisState.Sanitize() + + // load the accounts + for _, gacc := range genesisState { + acc := gacc.ToAccount() + acc = accountKeeper.NewAccount(ctx, acc) // set account number + accountKeeper.SetAccount(ctx, acc) + } +} diff --git a/x/genaccounts/module.go b/x/genaccounts/module.go new file mode 100644 index 000000000..351ebd405 --- /dev/null +++ b/x/genaccounts/module.go @@ -0,0 +1,104 @@ +package genaccounts + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/terra-project/core/x/auth" +) + +var ( + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// module name +const ModuleName = "accounts" + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return ModuleName +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return moduleCdc.MustMarshalJSON(GenesisState{}) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := moduleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } + +// extra function from sdk.AppModuleBasic +// iterate the genesis accounts and perform an operation at each of them +// - to used by other modules +func (AppModuleBasic) IterateGenesisAccounts(cdc *codec.Codec, appGenesis map[string]json.RawMessage, + iterateFn func(auth.Account) (stop bool)) { + + genesisState := GetGenesisStateFromAppState(cdc, appGenesis) + for _, genAcc := range genesisState { + acc := genAcc.ToAccount() + if iterateFn(acc) { + break + } + } +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + accountKeeper AccountKeeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(accountKeeper AccountKeeper) module.AppModule { + + return module.NewGenesisOnlyAppModule(AppModule{ + AppModuleBasic: AppModuleBasic{}, + accountKeeper: accountKeeper, + }) +} + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + moduleCdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, moduleCdc, am.accountKeeper, genesisState) + return []abci.ValidatorUpdate{} +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + gs := ExportGenesis(ctx, am.accountKeeper) + return moduleCdc.MustMarshalJSON(gs) +} diff --git a/x/genutil/alias.go b/x/genutil/alias.go new file mode 100644 index 000000000..bc831f9e6 --- /dev/null +++ b/x/genutil/alias.go @@ -0,0 +1,19 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/genutil/internal/types/ +package genutil + +import ( + "github.com/terra-project/core/x/genutil/internal/types" +) + +var ( + ModuleCdc = types.ModuleCdc +) + +type ( + StakingKeeper = types.StakingKeeper + AccountKeeper = types.AccountKeeper + GenesisAccountsIterator = types.GenesisAccountsIterator +) diff --git a/x/genutil/cosmos_alias.go b/x/genutil/cosmos_alias.go new file mode 100644 index 000000000..3cad242c7 --- /dev/null +++ b/x/genutil/cosmos_alias.go @@ -0,0 +1,29 @@ +// nolint +package genutil + +import ( + "github.com/cosmos/cosmos-sdk/x/genutil" +) + +const ( + ModuleName = genutil.ModuleName +) + +var ( + // functions aliases + NewCosmosAppModule = genutil.NewAppModule + InitGenesis = genutil.InitGenesis + InitializeNodeValidatorFiles = genutil.InitializeNodeValidatorFiles + NewInitConfig = genutil.NewInitConfig + ExportGenesisFileWithTime = genutil.ExportGenesisFileWithTime + GenesisStateFromGenFile = genutil.GenesisStateFromGenFile + GenAppStateFromConfig = genutil.GenAppStateFromConfig + ExportGenesisFile = genutil.ExportGenesisFile + CosmosModuleCdc = genutil.ModuleCdc +) + +type ( + GenesisState = genutil.GenesisState + CosmosAppModule = genutil.AppModule + CosmosAppModuleBasic = genutil.AppModuleBasic +) diff --git a/x/genutil/genesis_state.go b/x/genutil/genesis_state.go new file mode 100644 index 000000000..6d5f3ecda --- /dev/null +++ b/x/genutil/genesis_state.go @@ -0,0 +1,32 @@ +package genutil + +import ( + "errors" + "fmt" + + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/terra-project/core/x/auth" +) + +// validate GenTx transactions +func ValidateGenesis(genesisState GenesisState) error { + for i, genTx := range genesisState.GenTxs { + var tx auth.StdTx + if err := ModuleCdc.UnmarshalJSON(genTx, &tx); err != nil { + return err + } + + msgs := tx.GetMsgs() + if len(msgs) != 1 { + return errors.New( + "must provide genesis StdTx with exactly 1 CreateValidator message") + } + + // TODO abstract back to staking + if _, ok := msgs[0].(staking.MsgCreateValidator); !ok { + return fmt.Errorf( + "Genesis transaction %v does not contain a MsgCreateValidator", i) + } + } + return nil +} diff --git a/x/genutil/internal/types/codec.go b/x/genutil/internal/types/codec.go new file mode 100644 index 000000000..422e7b91a --- /dev/null +++ b/x/genutil/internal/types/codec.go @@ -0,0 +1,23 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/auth" + "github.com/terra-project/core/x/staking" +) + +// generic sealed codec to be used throughout this module +var ModuleCdc *codec.Codec + +// TODO abstract genesis transactions registration back to staking +// required for genesis transactions +func init() { + ModuleCdc = codec.New() + staking.RegisterCodec(ModuleCdc) + auth.RegisterCodec(ModuleCdc) + sdk.RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/genutil/internal/types/expected_keepers.go b/x/genutil/internal/types/expected_keepers.go new file mode 100644 index 000000000..98e720b9b --- /dev/null +++ b/x/genutil/internal/types/expected_keepers.go @@ -0,0 +1,32 @@ +package types + +import ( + "encoding/json" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/auth" +) + +// expected staking keeper +type StakingKeeper interface { + ApplyAndReturnValidatorSetUpdates(sdk.Context) (updates []abci.ValidatorUpdate) +} + +// expected account keeper +type AccountKeeper interface { + NewAccount(sdk.Context, auth.Account) auth.Account + SetAccount(sdk.Context, auth.Account) + IterateAccounts(ctx sdk.Context, process func(auth.Account) (stop bool)) +} + +// The expected interface for iterating genesis accounts object +type GenesisAccountsIterator interface { + IterateGenesisAccounts( + cdc *codec.Codec, + appGenesis map[string]json.RawMessage, + iterateFn func(auth.Account) (stop bool), + ) +} diff --git a/x/genutil/module.go b/x/genutil/module.go new file mode 100644 index 000000000..9a7bf1ffa --- /dev/null +++ b/x/genutil/module.go @@ -0,0 +1,135 @@ +// Package genutil has moduleCdc, we have to implement codec used part same as origin +package genutil + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(GenesisState{}) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + accountKeeper AccountKeeper + stakingKeeper StakingKeeper + deliverTx func(abci.RequestDeliverTx) abci.ResponseDeliverTx + cosmosAppModule module.AppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(accountKeeper AccountKeeper, + stakingKeeper StakingKeeper, deliverTx func(abci.RequestDeliverTx) abci.ResponseDeliverTx) module.AppModule { + + return module.NewGenesisOnlyAppModule(AppModule{ + AppModuleBasic: AppModuleBasic{}, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + deliverTx: deliverTx, + cosmosAppModule: NewCosmosAppModule(accountKeeper, stakingKeeper, deliverTx), + }) +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + return InitGenesis(ctx, ModuleCdc, am.stakingKeeper, am.deliverTx, genesisState) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/market/abci.go b/x/market/abci.go new file mode 100644 index 000000000..dc1140259 --- /dev/null +++ b/x/market/abci.go @@ -0,0 +1,25 @@ +package market + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/types" +) + +// EndBlocker is called at the end of every block +func EndBlocker(ctx sdk.Context, k Keeper) { + if !core.IsPeriodLastBlock(ctx, core.BlocksPerDay) { + return + } + + // update luna issuance at last block of a day + updatedIssuance := k.UpdatePrevDayIssuance(ctx) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventDaliyIssuanceUpdate, + sdk.NewAttribute(types.AttributeKeyIssuance, updatedIssuance.String()), + ), + ) +} diff --git a/x/market/abci_test.go b/x/market/abci_test.go new file mode 100644 index 000000000..a58d1bcea --- /dev/null +++ b/x/market/abci_test.go @@ -0,0 +1,25 @@ +package market + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/keeper" +) + +func TestOracleThreshold(t *testing.T) { + input := keeper.CreateTestInput(t) + + targetIssuance := sdk.NewInt(1000000) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, targetIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerDay - 1) + EndBlocker(input.Ctx, input.MarketKeeper) + issuance := input.MarketKeeper.GetPrevDayIssuance(input.Ctx).AmountOf(core.MicroLunaDenom) + require.Equal(t, targetIssuance, issuance) +} diff --git a/x/market/alias.go b/x/market/alias.go new file mode 100644 index 000000000..617d1bb40 --- /dev/null +++ b/x/market/alias.go @@ -0,0 +1,65 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/market/internal/types/ +// ALIASGEN: github.com/terra-project/core/x/market/internal/keeper/ +package market + +import ( + "github.com/terra-project/core/x/market/internal/keeper" + "github.com/terra-project/core/x/market/internal/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + CodeInsufficientSwap = types.CodeInsufficientSwap + CodeNoEffectivePrice = types.CodeNoEffectivePrice + CodeRecursiveSwap = types.CodeRecursiveSwap + CodeExceedsSwapLimit = types.CodeExceedsSwapLimit + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + DefaultParamspace = types.DefaultParamspace + QuerySwap = types.QuerySwap + QueryPrevDayIssuance = types.QueryPrevDayIssuance + QueryParameters = types.QueryParameters +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + ErrNoEffectivePrice = types.ErrNoEffectivePrice + ErrInsufficientSwapCoins = types.ErrInsufficientSwapCoins + ErrRecursiveSwap = types.ErrRecursiveSwap + ErrExceedsDailySwapLimit = types.ErrExceedsDailySwapLimit + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis + NewMsgSwap = types.NewMsgSwap + DefaultParams = types.DefaultParams + NewQuerySwapParams = types.NewQuerySwapParams + NewKeeper = keeper.NewKeeper + ParamKeyTable = keeper.ParamKeyTable + NewQuerier = keeper.NewQuerier + + // variable aliases + ModuleCdc = types.ModuleCdc + PrevDayIssuanceKey = types.PrevDayIssuanceKey + ParamStoreKeyDailyLunaDeltaCap = types.ParamStoreKeyDailyLunaDeltaCap + ParamStoreKeyMaxSwapSpread = types.ParamStoreKeyMaxSwapSpread + ParamStoreKeyMinSwapSpread = types.ParamStoreKeyMinSwapSpread + DefaultDailyLunaDeltaCap = types.DefaultDailyLunaDeltaCap + DefaultMaxSwapSpread = types.DefaultMaxSwapSpread + DefaultMinSwapSpread = types.DefaultMinSwapSpread +) + +type ( + SupplyKeeper = types.SupplyKeeper + OracleKeeper = types.OracleKeeper + GenesisState = types.GenesisState + MsgSwap = types.MsgSwap + Params = types.Params + QuerySwapParams = types.QuerySwapParams + Keeper = keeper.Keeper +) diff --git a/x/market/client/cli/cli_test.go b/x/market/client/cli/cli_test.go deleted file mode 100644 index 63bb969fe..000000000 --- a/x/market/client/cli/cli_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/spf13/cobra" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" - "github.com/terra-project/core/x/market" - - "github.com/cosmos/cosmos-sdk/client" -) - -func TestSwapTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - marketTxCmd := &cobra.Command{ - Use: "market", - Short: "Market transaction subcommands", - } - - txCmd.AddCommand(marketTxCmd) - - marketTxCmd.AddCommand(client.PostCommands( - GetSwapCmd(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `market`, - `swap`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--offer-coin=1000uluna`, - `--ask-denom=ukrw`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestQuerySwap(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - querySwapCmd := GetCmdQuerySwap(cdc) - - // Name check - require.Equal(t, market.QuerySwap, querySwapCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(querySwapCmd.Args)) - - // Check Flags - askDenomFlag := querySwapCmd.Flag(flagAskDenom) - require.NotNil(t, askDenomFlag) - require.Equal(t, []string{"true"}, askDenomFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - offerCoinFlag := querySwapCmd.Flag(flagOfferCoin) - require.NotNil(t, offerCoinFlag) - require.Equal(t, []string{"true"}, offerCoinFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParamsCmd := GetCmdQueryParams(cdc) - - // Name check - require.Equal(t, market.QueryParams, queryParamsCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParamsCmd.Args)) -} diff --git a/x/market/client/cli/query.go b/x/market/client/cli/query.go index 060f1946a..e2b5cb292 100644 --- a/x/market/client/cli/query.go +++ b/x/market/client/cli/query.go @@ -4,81 +4,112 @@ import ( "fmt" "strings" - "github.com/spf13/viper" - - "github.com/terra-project/core/x/market" - "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/market/internal/types" ) +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + marketQueryCmd := &cobra.Command{ + Use: "market", + Short: "Querying commands for the market module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + marketQueryCmd.AddCommand(client.GetCommands( + GetCmdQuerySwap(queryRoute, cdc), + GetCmdQueryParams(queryRoute, cdc), + GetCmdQueryPrevDayIssuance(queryRoute, cdc), + )...) + + return marketQueryCmd +} + // GetCmdQuerySwap implements the query swap amount command. -func GetCmdQuerySwap(cdc *codec.Codec) *cobra.Command { +func GetCmdQuerySwap(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "swap", - Args: cobra.NoArgs, + Use: "swap [offer-coin] [ask-denom]", + Args: cobra.ExactArgs(2), Short: "Query a quote for a swap operation", Long: strings.TrimSpace(` Query a quote for how many coins can be received in a swap operation. Note; rates are dynamic and can quickly change. -$ terracli query query swap --ask-denom usdr --offer-coin 5000000uluna +$ terracli query query swap 5000000uluna usdr `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - askDenom := viper.GetString(flagAskDenom) - // parse offerCoin - offerCoinStr := viper.GetString(flagOfferCoin) + offerCoinStr := args[0] offerCoin, err := sdk.ParseCoin(offerCoinStr) if err != nil { return err } - params := market.NewQuerySwapParams(offerCoin) + askDenom := args[1] + + params := types.NewQuerySwapParams(offerCoin, askDenom) bz := cdc.MustMarshalJSON(params) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", market.QuerierRoute, market.QuerySwap, askDenom), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", queryRoute, types.QuerySwap, askDenom), bz) if err != nil { return err } var retCoin sdk.Coin - err = cdc.UnmarshalJSON(res, &retCoin) + cdc.MustUnmarshalBinaryLengthPrefixed(res, &retCoin) + return cliCtx.PrintOutput(retCoin) + }, + } + + return cmd +} + +// GetCmdQueryPrevDayIssuance implements the query params command. +func GetCmdQueryPrevDayIssuance(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "prev-day-issuance", + Args: cobra.ExactArgs(1), + Short: "Query the prev day issuance", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryPrevDayIssuance), nil) if err != nil { return err } - return cliCtx.PrintOutput(retCoin) + var prevDayIssuance sdk.Coins + cdc.MustUnmarshalJSON(res, &prevDayIssuance) + return cliCtx.PrintOutput(prevDayIssuance) }, } - cmd.Flags().String(flagAskDenom, "", "Denom of the asset to swap to") - cmd.Flags().String(flagOfferCoin, "", "The asset to swap from e.g. 1000ukrw") - - cmd.MarkFlagRequired(flagAskDenom) - cmd.MarkFlagRequired(flagOfferCoin) - return cmd } // GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { +func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: market.QueryParams, + Use: "params", Args: cobra.NoArgs, Short: "Query the current market params", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", market.QuerierRoute, market.QueryParams), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryParameters), nil) if err != nil { return err } - var params market.Params + var params types.Params cdc.MustUnmarshalJSON(res, ¶ms) return cliCtx.PrintOutput(params) }, diff --git a/x/market/client/cli/tx.go b/x/market/client/cli/tx.go index 147a052e5..6ba7eb38e 100644 --- a/x/market/client/cli/tx.go +++ b/x/market/client/cli/tx.go @@ -1,93 +1,72 @@ package cli import ( - "fmt" "strings" - "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/market/internal/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -const ( - flagOfferCoin = "offer-coin" - flagAskDenom = "ask-denom" - flagOffline = "offline" -) +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + marketTxCmd := &cobra.Command{ + Use: "market", + Short: "Market transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + marketTxCmd.AddCommand(client.PostCommands( + GetSwapCmd(cdc), + )...) + + return marketTxCmd +} // GetSwapCmd will create and send a MsgSwap func GetSwapCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "swap", + Use: "swap [offer-coin] [ask-denom]", + Args: cobra.ExactArgs(2), Short: "Atomically swap currencies at their target exchange rate", Long: strings.TrimSpace(` Swap the offer-coin to the ask-denom currency at the oracle's effective exchange rate. -$ terracli market swap --offer-coin="1000ukrw" --ask-denom="uusd" +$ terracli market swap "1000ukrw" "uusd" `), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - - askDenom := viper.GetString(flagAskDenom) - if len(askDenom) == 0 { - return fmt.Errorf("--ask-denom flag is required") - } - - offerCoinStr := viper.GetString(flagOfferCoin) - if len(offerCoinStr) == 0 { - return fmt.Errorf("--offer-coin flag is required") - } + cliCtx := context.NewCLIContext().WithCodec(cdc) + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + offerCoinStr := args[0] offerCoin, err := sdk.ParseCoin(offerCoinStr) if err != nil { return err } - fromAddress := cliCtx.GetFromAddress() - - offline := viper.GetBool(flagOffline) - if !offline { - fromAccount, err := cliCtx.GetAccount(fromAddress) - if err != nil { - return err - } + askDenom := args[1] - if fromAccount.GetCoins().AmountOf(offerCoin.Denom).LT(offerCoin.Amount) { - return fmt.Errorf(strings.TrimSpace(` - account %s has insufficient amount of coins to pay the offered coins.\n - Required: %s\n - Given: %s\n`), - fromAddress, offerCoin, fromAccount.GetCoins()) - } - } + fromAddress := cliCtx.GetFromAddress() // build and sign the transaction, then broadcast to Tendermint - msg := market.NewMsgSwap(fromAddress, offerCoin, askDenom) + msg := types.NewMsgSwap(fromAddress, offerCoin, askDenom) err = msg.ValidateBasic() if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - cmd.Flags().String(flagOfferCoin, "", "The asset to swap from e.g. 1000ukrw") - cmd.Flags().String(flagAskDenom, "", "Denom of the asset to swap to") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - cmd.MarkFlagRequired(flagOfferCoin) - cmd.MarkFlagRequired(flagAskDenom) - return cmd } diff --git a/x/market/client/module_client.go b/x/market/client/module_client.go deleted file mode 100644 index 895f2897c..000000000 --- a/x/market/client/module_client.go +++ /dev/null @@ -1,48 +0,0 @@ -package client - -import ( - "github.com/terra-project/core/x/market/client/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - marketQueryCmd := &cobra.Command{ - Use: "market", - Short: "Querying commands for the market module", - } - - marketQueryCmd.AddCommand(client.GetCommands( - cli.GetCmdQuerySwap(mc.cdc), - cli.GetCmdQueryParams(mc.cdc), - )...) - - return marketQueryCmd -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - marketTxCmd := &cobra.Command{ - Use: "market", - Short: "Market transaction subcommands", - } - - marketTxCmd.AddCommand(client.PostCommands( - cli.GetSwapCmd(mc.cdc), - )...) - - return marketTxCmd -} diff --git a/x/market/client/module_client_test.go b/x/market/client/module_client_test.go deleted file mode 100644 index 81f518487..000000000 --- a/x/market/client/module_client_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - txCmdList = map[string]bool{ - "swap": true, - } -) - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/market/client/rest/query.go b/x/market/client/rest/query.go index 24c9bd5b6..0f3de2c4d 100644 --- a/x/market/client/rest/query.go +++ b/x/market/client/rest/query.go @@ -5,23 +5,27 @@ import ( "fmt" "net/http" - "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/market/internal/types" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/gorilla/mux" sdk "github.com/cosmos/cosmos-sdk/types" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/market/swap", querySwapHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/market/params", queryParamsHandlerFn(cdc, cliCtx)).Methods("GET") +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/market/swap", querySwapHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc("/market/prev_day_issuance", queryPrevDayIssuanceHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc("/market/parameters", queryParamsHandlerFn(cliCtx)).Methods("GET") } -func querySwapHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func querySwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } err := r.ParseForm() if err != nil { @@ -46,26 +50,51 @@ func querySwapHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle return } - params := market.NewQuerySwapParams(offerCoin) - bz := cdc.MustMarshalJSON(params) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", market.QuerierRoute, market.QuerySwap, askDenom), bz) + params := types.NewQuerySwapParams(offerCoin, askDenom) + bz := cliCtx.Codec.MustMarshalJSON(params) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySwap), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + +func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +func queryPrevDayIssuanceHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", market.QuerierRoute, market.QueryParams), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPrevDayIssuance), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } diff --git a/x/market/client/rest/rest.go b/x/market/client/rest/rest.go index f23009417..47f5ba98b 100644 --- a/x/market/client/rest/rest.go +++ b/x/market/client/rest/rest.go @@ -2,12 +2,14 @@ package rest import ( "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" "github.com/gorilla/mux" ) +// RestDenom +const RestDenom = "denom" + // RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - registerTxRoutes(cliCtx, r, cdc) - registerQueryRoutes(cliCtx, r, cdc) +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + registerTxRoutes(cliCtx, r) + registerQueryRoutes(cliCtx, r) } diff --git a/x/market/client/rest/tx.go b/x/market/client/rest/tx.go index d9c109b21..d246ab1a1 100644 --- a/x/market/client/rest/tx.go +++ b/x/market/client/rest/tx.go @@ -1,24 +1,20 @@ package rest import ( - "fmt" "net/http" - "strings" - "github.com/terra-project/core/x/market" - - clientrest "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/terra-project/core/x/market/internal/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/market/swap", submitSwapHandlerFn(cdc, cliCtx)).Methods("POST") +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/market/swap", submitSwapHandlerFn(cliCtx)).Methods("POST") } //nolint @@ -29,10 +25,10 @@ type SwapReq struct { } // submitSwapHandlerFn handles a POST vote request -func submitSwapHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func submitSwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req SwapReq - if !rest.ReadRESTReq(w, r, cdc, &req) { + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { err := sdk.ErrUnknownRequest("malformed request") rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -51,30 +47,14 @@ func submitSwapHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handl return } - fromAccount, err := cliCtx.GetAccount(fromAddress) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - if fromAccount.GetCoins().AmountOf(req.OfferCoin.Denom).LT(req.OfferCoin.Amount) { - err := fmt.Errorf(strings.TrimSpace(` - account %s has insufficient amount of coins to pay the offered coins.\n - Required: %s\n - Given: %s\n`), fromAddress, req.OfferCoin, fromAccount.GetCoins()) - - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - // create the message - msg := market.NewMsgSwap(fromAddress, req.OfferCoin, req.AskDenom) + msg := types.NewMsgSwap(fromAddress, req.OfferCoin, req.AskDenom) err = msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) } } diff --git a/x/market/constants.go b/x/market/constants.go deleted file mode 100644 index 03eb71984..000000000 --- a/x/market/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package market - -const ( - // ModuleName is the name of the market module - ModuleName = "market" - - // StoreKey is the string store representation - StoreKey = ModuleName - - // RouterKey is the msg router key for the market module - RouterKey = ModuleName - - // QuerierRoute is the query router key for the oracle module - QuerierRoute = ModuleName - - // DefaultParamspace is for the paramspace notation - DefaultParamspace = ModuleName -) diff --git a/x/market/expected_keeper.go b/x/market/expected_keeper.go deleted file mode 100644 index e94bf7528..000000000 --- a/x/market/expected_keeper.go +++ /dev/null @@ -1,16 +0,0 @@ -package market - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// expected oracle keeper -type OracleKeeper interface { - AddSwapFeePool(ctx sdk.Context, fees sdk.Coins) - GetLunaSwapRate(ctx sdk.Context, denom string) (price sdk.Dec, err sdk.Error) -} - -// expected mint keeper -type MintKeeper interface { - Mint(ctx sdk.Context, recipient sdk.AccAddress, coin sdk.Coin) (err sdk.Error) - Burn(ctx sdk.Context, payer sdk.AccAddress, coin sdk.Coin) (err sdk.Error) - GetIssuance(ctx sdk.Context, denom string, day sdk.Int) (issuance sdk.Int) -} diff --git a/x/market/genesis.go b/x/market/genesis.go index 5680604b5..ad5f41f47 100644 --- a/x/market/genesis.go +++ b/x/market/genesis.go @@ -4,38 +4,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// GenesisState - all distribution state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params"` // market params -} - -func NewGenesisState(params Params) GenesisState { - return GenesisState{ - Params: params, - } -} - -// get raw genesis raw message for testing -func DefaultGenesisState() GenesisState { - return GenesisState{ - Params: DefaultParams(), - } -} - -// new oracle genesis +// InitGenesis initialize default parameters +// and the keeper's address to pubkey map func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetParams(ctx, data.Params) } -// ExportGenesis returns a GenesisState for a given context and keeper. The -// GenesisState will contain the pool, and validator/delegator distribution info's -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { +// ExportGenesis writes the current store values +// to a genesis file, which can be imported again +// with InitGenesis +func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { params := keeper.GetParams(ctx) - return NewGenesisState(params) -} -// ValidateGenesis validates the provided oracle genesis state to ensure the -// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) -func ValidateGenesis(data GenesisState) error { - return validateParams(data.Params) + return NewGenesisState(params) } diff --git a/x/market/handler.go b/x/market/handler.go index 92e4409d1..f6870890d 100644 --- a/x/market/handler.go +++ b/x/market/handler.go @@ -3,9 +3,8 @@ package market import ( "reflect" - "github.com/terra-project/core/x/market/tags" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/market/internal/types" ) // NewHandler creates a new handler for all market type messages. @@ -22,52 +21,67 @@ func NewHandler(k Keeper) sdk.Handler { } // handleMsgSwap handles the logic of a MsgSwap -func handleMsgSwap(ctx sdk.Context, k Keeper, msg MsgSwap) sdk.Result { +func handleMsgSwap(ctx sdk.Context, k Keeper, ms MsgSwap) sdk.Result { // Can't swap to the same coin - if msg.OfferCoin.Denom == msg.AskDenom { - return ErrRecursiveSwap(DefaultCodespace, msg.AskDenom).Result() + if ms.OfferCoin.Denom == ms.AskDenom { + return ErrRecursiveSwap(DefaultCodespace, ms.AskDenom).Result() } // Compute exchange rates between the ask and offer - swapCoin, spread, swapErr := k.GetSwapCoin(ctx, msg.OfferCoin, msg.AskDenom, false) + swapCoin, spread, swapErr := k.GetSwapCoin(ctx, ms.OfferCoin, ms.AskDenom, false) if swapErr != nil { return swapErr.Result() } + // Send offer coins to module account + offerCoins := sdk.NewCoins(ms.OfferCoin) + err := k.SupplyKeeper.SendCoinsFromAccountToModule(ctx, ms.Trader, ModuleName, offerCoins) + if err != nil { + return err.Result() + } + // Charge a spread if applicable; distributed to vote winners in the oracle module - swapFee := sdk.Coin{} + var swapFee sdk.Coin if spread.IsPositive() { swapFeeAmt := spread.MulInt(swapCoin.Amount).TruncateInt() if swapFeeAmt.IsPositive() { swapFee = sdk.NewCoin(swapCoin.Denom, swapFeeAmt) - k.ok.AddSwapFeePool(ctx, sdk.NewCoins(swapFee)) - swapCoin = swapCoin.Sub(swapFee) } } // Burn offered coins and subtract from the trader's account - burnErr := k.mk.Burn(ctx, msg.Trader, msg.OfferCoin) + burnErr := k.SupplyKeeper.BurnCoins(ctx, ModuleName, offerCoins) if burnErr != nil { return burnErr.Result() } // Mint asked coins and credit Trader's account - mintErr := k.mk.Mint(ctx, msg.Trader, swapCoin) + swapCoins := sdk.NewCoins(swapCoin) + mintErr := k.SupplyKeeper.MintCoins(ctx, ModuleName, swapCoins) if mintErr != nil { return mintErr.Result() } - log := NewLog() - log = log.append(LogKeySwapCoin, swapCoin.String()) - log = log.append(LogKeySwapFee, swapFee.String()) + sendErr := k.SupplyKeeper.SendCoinsFromModuleToAccount(ctx, ModuleName, ms.Trader, swapCoins) + if sendErr != nil { + return sendErr.Result() + } - return sdk.Result{ - Tags: sdk.NewTags( - tags.Offer, msg.OfferCoin.Denom, - tags.Trader, msg.Trader.String(), + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventSwap, + sdk.NewAttribute(types.AttributeKeyOffer, ms.OfferCoin.String()), + sdk.NewAttribute(types.AttributeKeyTrader, ms.Trader.String()), + sdk.NewAttribute(types.AttributeKeySwapCoin, swapCoin.String()), + sdk.NewAttribute(types.AttributeKeySwapFee, swapFee.String()), ), - Log: log.String(), - } + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } diff --git a/x/market/handler_test.go b/x/market/handler_test.go index 5fe15a7d5..c83d21f2c 100644 --- a/x/market/handler_test.go +++ b/x/market/handler_test.go @@ -3,126 +3,35 @@ package market import ( "testing" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestHandlerMsgSwapValidPrice(t *testing.T) { - input := createTestInput(t) - handler := NewHandler(input.marketKeeper) - - lnasdrRate := sdk.NewDec(4) - lnacnyRate := sdk.NewDec(8) - offerCoin := sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(2).MulRaw(assets.MicroUnit)) - askCoin := sdk.NewCoin(assets.MicroCNYDenom, sdk.NewInt(4).MulRaw(assets.MicroUnit)) - - msg := NewMsgSwap(addrs[0], offerCoin, askCoin.Denom) - - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - // Set offer asset price - input.oracleKeeper.SetLunaSwapRate(input.ctx, offerCoin.Denom, lnasdrRate) - - res = handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - // Set ask asset price - input.oracleKeeper.SetLunaSwapRate(input.ctx, askCoin.Denom, lnacnyRate) - - res = handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - retAmt := lnacnyRate.Quo(lnasdrRate).MulInt(offerCoin.Amount).TruncateInt() - trader := input.accKeeper.GetAccount(input.ctx, addrs[0]) - require.Equal(t, trader.GetCoins().AmountOf(offerCoin.Denom), uSDRAmt.Sub(offerCoin.Amount)) - require.Equal(t, trader.GetCoins().AmountOf(askCoin.Denom), retAmt) -} - -func TestHandlerMsgSwapNoBalance(t *testing.T) { - input := createTestInput(t) - handler := NewHandler(input.marketKeeper) - - // Try to swap a coin I don't have at all - msg := NewMsgSwap(addrs[0], sdk.NewCoin(assets.MicroCNYDenom, sdk.OneInt().MulRaw(assets.MicroUnit)), assets.MicroGBPDenom) - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - // Try to swap a coin I don't have enough of - msg.OfferCoin = sdk.NewCoin(assets.MicroSDRDenom, uSDRAmt.Add(sdk.OneInt().MulRaw(assets.MicroUnit))) - res = handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) -} - -func TestHandlerMsgSwapRecursion(t *testing.T) { - input := createTestInput(t) - handler := NewHandler(input.marketKeeper) + "github.com/cosmos/cosmos-sdk/x/bank" - msg := NewMsgSwap(addrs[0], sdk.NewCoin(assets.MicroSDRDenom, sdk.OneInt().MulRaw(assets.MicroUnit)), assets.MicroSDRDenom) - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) -} - -func TestHandlerMsgSwapTooSmall(t *testing.T) { - input := createTestInput(t) - handler := NewHandler(input.marketKeeper) - - offerCoin := sdk.NewCoin(assets.MicroSDRDenom, sdk.NewDecFromIntWithPrec(sdk.OneInt(), 4).MulInt64(assets.MicroUnit).TruncateInt()) - askDenom := assets.MicroCNYDenom - askLunaPrice := sdk.NewDec(1) - offerLunaPrice := sdk.NewDecWithPrec(1001, 1) - - // Set oracle price - input.oracleKeeper.SetLunaSwapRate(input.ctx, offerCoin.Denom, offerLunaPrice) - input.oracleKeeper.SetLunaSwapRate(input.ctx, askDenom, askLunaPrice) - - msg := NewMsgSwap(addrs[0], offerCoin, askDenom) - - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - // Reset oracle price - askLunaPrice = sdk.NewDecWithPrec(1000, 1) - input.oracleKeeper.SetLunaSwapRate(input.ctx, askDenom, askLunaPrice) - - res = handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) -} - -func TestHandlerExceedDailySwapLimit(t *testing.T) { - input := createTestInput(t) - handler := NewHandler(input.marketKeeper) - - offerCoin := sdk.NewInt64Coin(assets.MicroSDRDenom, 1000) + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/keeper" +) - // Set oracle price - offerLunaPrice := sdk.NewDec(1) - input.oracleKeeper.SetLunaSwapRate(input.ctx, offerCoin.Denom, offerLunaPrice) +func TestMarketFilters(t *testing.T) { + input, h := setup(t) - // Day 0 ... trade goes through, even though Luna doesn't even have a stated issuance. - msg := NewMsgSwap(addrs[0], offerCoin, assets.MicroLunaDenom) - res := handler(input.ctx, msg) - require.True(t, res.IsOK()) + // Case 1: non-oracle message being sent fails + bankMsg := bank.MsgSend{} + res := h(input.Ctx, bankMsg) + require.False(t, res.IsOK()) - // Day 1+ ... Set luna issuance, try to oscillate within the limit, and things should be ok - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerWeek) - offerCoin = sdk.NewCoin(offerCoin.Denom, sdk.NewInt(4)) - msg = NewMsgSwap(addrs[0], offerCoin, assets.MicroLunaDenom) - res = handler(input.ctx, msg) + // Case 2: Normal MsgSwap submission goes through + offerCoin := sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(10)) + prevoteMsg := NewMsgSwap(keeper.Addrs[0], offerCoin, core.MicroSDRDenom) + res = h(input.Ctx, prevoteMsg) require.True(t, res.IsOK()) +} - // Day 1+ ... Outside of the limit fails - msg = NewMsgSwap(addrs[0], sdk.NewInt64Coin(assets.MicroLunaDenom, 6), assets.MicroLunaDenom) - res = handler(input.ctx, msg) - require.False(t, res.IsOK()) +func TestSwapMsg(t *testing.T) { + input, h := setup(t) - // Swapping Terra with each other should be unlimited - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, sdk.OneDec()) - msg = NewMsgSwap(addrs[1], sdk.NewCoin(assets.MicroSDRDenom, uSDRAmt), assets.MicroCNYDenom) // 1/3 of SDR issuance - res = handler(input.ctx, msg) + offerCoin := sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(10)) + prevoteMsg := NewMsgSwap(keeper.Addrs[0], offerCoin, core.MicroSDRDenom) + res := h(input.Ctx, prevoteMsg) require.True(t, res.IsOK()) } diff --git a/x/market/internal/keeper/keeper.go b/x/market/internal/keeper/keeper.go new file mode 100644 index 000000000..1b505f7ac --- /dev/null +++ b/x/market/internal/keeper/keeper.go @@ -0,0 +1,167 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/types" +) + +// Keeper of the oracle store +type Keeper struct { + cdc *codec.Codec + storeKey sdk.StoreKey + paramSpace params.Subspace + + oracleKeeper types.OracleKeeper + SupplyKeeper types.SupplyKeeper + + // codespace + codespace sdk.CodespaceType +} + +// NewKeeper constructs a new keeper for oracle +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, + paramspace params.Subspace, oracleKeeper types.OracleKeeper, + supplyKeeper types.SupplyKeeper, codespace sdk.CodespaceType) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + paramSpace: paramspace.WithKeyTable(ParamKeyTable()), + oracleKeeper: oracleKeeper, + SupplyKeeper: supplyKeeper, + codespace: codespace, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// Codespace returns a codespace of keeper +func (k Keeper) Codespace() sdk.CodespaceType { + return k.codespace +} + +// GetPrevDayIssuance returns the prev day issuance +func (k Keeper) GetPrevDayIssuance(ctx sdk.Context) (issuance sdk.Coins) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PrevDayIssuanceKey) + if bz == nil { + return sdk.Coins{} + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &issuance) + return +} + +// UpdatePrevDayIssuance stores the prev day issuance +func (k Keeper) UpdatePrevDayIssuance(ctx sdk.Context) sdk.Coins { + store := ctx.KVStore(k.storeKey) + + totalCoins := k.SupplyKeeper.GetSupply(ctx).GetTotal() + bz := k.cdc.MustMarshalBinaryLengthPrefixed(totalCoins) + store.Set(types.PrevDayIssuanceKey, bz) + + return totalCoins +} + +// ComputeLunaDelta returns the issuance change rate of Luna for the day post-swap +func (k Keeper) ComputeLunaDelta(ctx sdk.Context, change sdk.Int) sdk.Dec { + curDay := ctx.BlockHeight() / core.BlocksPerDay + if curDay == 0 { + return sdk.ZeroDec() + } + + lastDayLunaIssuance := k.GetPrevDayIssuance(ctx).AmountOf(core.MicroLunaDenom) + if lastDayLunaIssuance.IsZero() { + return sdk.ZeroDec() + } + + supply := k.SupplyKeeper.GetSupply(ctx) + lunaIssuance := supply.GetTotal().AmountOf(core.MicroLunaDenom) + + postSwapIssunace := lunaIssuance.Add(change) + + return sdk.NewDecFromInt(postSwapIssunace.Sub(lastDayLunaIssuance)).QuoInt(lastDayLunaIssuance) +} + +// ComputeLunaSwapSpread returns a spread, which is initialiy MinSwapSpread and grows linearly to MaxSwapSpread with delta +func (k Keeper) ComputeLunaSwapSpread(ctx sdk.Context, postLunaDelta sdk.Dec) sdk.Dec { + if postLunaDelta.GTE(k.DailyLunaDeltaCap(ctx)) { + return k.MaxSwapSpread(ctx) + } + + // min + (p / l) (max - min); l = dailyDeltaCap, p = postDailyDelta, + return k.MinSwapSpread(ctx).Add(postLunaDelta.Quo(k.DailyLunaDeltaCap(ctx)).Mul(k.MaxSwapSpread(ctx).Sub(k.MinSwapSpread(ctx)))) +} + +// GetSwapCoin returns the amount of asked coins should be returned for a given offerCoin at the effective +// exchange rate registered with the oracle. +// Returns an Error if the swap is recursive, or the coins to be traded are unknown by the oracle, or the amount +// to trade is too small. +// Ignores caps and spreads if isInternal = true. +func (k Keeper) GetSwapCoin(ctx sdk.Context, offerCoin sdk.Coin, askDenom string, isInternal bool) (retCoin sdk.Coin, spread sdk.Dec, err sdk.Error) { + offerRate, err := k.oracleKeeper.GetLunaPrice(ctx, offerCoin.Denom) + if err != nil { + return sdk.Coin{}, sdk.ZeroDec(), types.ErrNoEffectivePrice(types.DefaultCodespace, offerCoin.Denom) + } + + askRate, err := k.oracleKeeper.GetLunaPrice(ctx, askDenom) + if err != nil { + return sdk.Coin{}, sdk.ZeroDec(), types.ErrNoEffectivePrice(types.DefaultCodespace, askDenom) + } + + retAmount := sdk.NewDecFromInt(offerCoin.Amount).Mul(askRate).Quo(offerRate).TruncateInt() + if retAmount.Equal(sdk.ZeroInt()) { + return sdk.Coin{}, sdk.ZeroDec(), types.ErrInsufficientSwapCoins(types.DefaultCodespace, offerCoin.Amount) + } + + // We only charge spread for NON-INTERNAL swaps involving luna; if not, just pass. + if isInternal || (offerCoin.Denom != core.MicroLunaDenom && askDenom != core.MicroLunaDenom) { + return sdk.NewCoin(askDenom, retAmount), sdk.ZeroDec(), nil + } + + dailyDelta := sdk.ZeroDec() + if offerCoin.Denom == core.MicroLunaDenom { + dailyDelta = k.ComputeLunaDelta(ctx, offerCoin.Amount.Neg()) + } else if askDenom == core.MicroLunaDenom { + dailyDelta = k.ComputeLunaDelta(ctx, retAmount) + } + + // delta should be positive to apply spread + dailyDelta = dailyDelta.Abs() + spread = k.ComputeLunaSwapSpread(ctx, dailyDelta) + + return sdk.NewCoin(askDenom, retAmount), spread, nil +} + +// GetSwapDecCoin returns the amount of asked DecCoins should be returned for a given offerCoin at the effective +// exchange rate registered with the oracle. +// Different from swapcoins, SwapDecCoins does not charge a spread as its use is system internal. +// Similar to SwapCoins, but operates over sdk.DecCoins for convenience and accuracy. +func (k Keeper) GetSwapDecCoin(ctx sdk.Context, offerCoin sdk.DecCoin, askDenom string) (sdk.DecCoin, sdk.Error) { + offerRate, err := k.oracleKeeper.GetLunaPrice(ctx, offerCoin.Denom) + if err != nil { + return sdk.DecCoin{}, types.ErrNoEffectivePrice(types.DefaultCodespace, offerCoin.Denom) + } + + askRate, err := k.oracleKeeper.GetLunaPrice(ctx, askDenom) + if err != nil { + return sdk.DecCoin{}, types.ErrNoEffectivePrice(types.DefaultCodespace, askDenom) + } + + retAmount := offerCoin.Amount.Mul(askRate).Quo(offerRate) + if retAmount.LTE(sdk.ZeroDec()) { + return sdk.DecCoin{}, types.ErrInsufficientSwapCoins(types.DefaultCodespace, offerCoin.Amount.TruncateInt()) + } + + return sdk.NewDecCoinFromDec(askDenom, retAmount), nil +} diff --git a/x/market/internal/keeper/keeper_test.go b/x/market/internal/keeper/keeper_test.go new file mode 100644 index 000000000..f8fe7ab1e --- /dev/null +++ b/x/market/internal/keeper/keeper_test.go @@ -0,0 +1,98 @@ +package keeper + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + core "github.com/terra-project/core/types" +) + +func TestPrevDayLunaIssuanceUpdate(t *testing.T) { + input := CreateTestInput(t) + + issuance := input.MarketKeeper.GetPrevDayIssuance(input.Ctx).AmountOf(core.MicroLunaDenom) + require.True(t, issuance.IsZero()) + + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.OneInt()))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.MarketKeeper.UpdatePrevDayIssuance(input.Ctx) + issuance = input.MarketKeeper.GetPrevDayIssuance(input.Ctx).AmountOf(core.MicroLunaDenom) + require.Equal(t, sdk.OneInt(), issuance) +} + +func TestComputeLunaDelta(t *testing.T) { + input := CreateTestInput(t) + + for i := 0; i < 100; i++ { + expectedDelta := sdk.NewDecWithPrec(rand.Int63n(1000), 3) + issuance := input.SupplyKeeper.GetSupply(input.Ctx).GetTotal().AmountOf(core.MicroLunaDenom) + change := expectedDelta.MulInt(issuance).TruncateInt() + input.MarketKeeper.UpdatePrevDayIssuance(input.Ctx) + delta := input.MarketKeeper.ComputeLunaDelta(input.Ctx.WithBlockHeight(core.BlocksPerDay), change) + + require.Equal(t, expectedDelta, delta) + } +} + +func TestComputeLunaSwapSpread(t *testing.T) { + input := CreateTestInput(t) + + for i := 0; i < 100; i++ { + delta := sdk.NewDecWithPrec(rand.Int63n(1000), 3) + spread := input.MarketKeeper.ComputeLunaSwapSpread(input.Ctx, delta) + require.True(t, spread.GTE(input.MarketKeeper.MinSwapSpread(input.Ctx))) + require.True(t, spread.LTE(input.MarketKeeper.MaxSwapSpread(input.Ctx))) + } + + spread := input.MarketKeeper.ComputeLunaSwapSpread(input.Ctx, sdk.ZeroDec()) + require.Equal(t, input.MarketKeeper.MinSwapSpread(input.Ctx), spread) + + spread = input.MarketKeeper.ComputeLunaSwapSpread(input.Ctx, sdk.OneDec()) + require.Equal(t, input.MarketKeeper.MaxSwapSpread(input.Ctx), spread) +} + +func TestGetSwapCoin(t *testing.T) { + input := CreateTestInput(t) + lunaPriceInSDR := sdk.NewDecWithPrec(17, 1) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, lunaPriceInSDR) + + // zero day (min spread) + for i := 0; i < 100; i++ { + offerCoin := sdk.NewCoin(core.MicroSDRDenom, lunaPriceInSDR.MulInt64(rand.Int63()+1).TruncateInt()) + retCoin, spread, err := input.MarketKeeper.GetSwapCoin(input.Ctx, offerCoin, core.MicroLunaDenom, false) + require.NoError(t, err) + require.Equal(t, input.MarketKeeper.MinSwapSpread(input.Ctx), spread) + require.Equal(t, sdk.NewDecFromInt(offerCoin.Amount).Quo(lunaPriceInSDR).TruncateInt(), retCoin.Amount) + + retCoin, spread, err = input.MarketKeeper.GetSwapCoin(input.Ctx, offerCoin, core.MicroLunaDenom, true) + require.NoError(t, err) + require.Equal(t, sdk.ZeroDec(), spread) + require.Equal(t, sdk.NewDecFromInt(offerCoin.Amount).Quo(lunaPriceInSDR).TruncateInt(), retCoin.Amount) + } + + offerCoin := sdk.NewCoin(core.MicroSDRDenom, lunaPriceInSDR.QuoInt64(2).TruncateInt()) + _, _, err := input.MarketKeeper.GetSwapCoin(input.Ctx, offerCoin, core.MicroLunaDenom, false) + require.Error(t, err) +} + +func TestGetDecSwapCoin(t *testing.T) { + input := CreateTestInput(t) + lunaPriceInSDR := sdk.NewDecWithPrec(17, 1) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, lunaPriceInSDR) + + // zero day (min spread) + for i := 0; i < 100; i++ { + offerCoin := sdk.NewDecCoin(core.MicroSDRDenom, lunaPriceInSDR.MulInt64(rand.Int63()+1).TruncateInt()) + retCoin, err := input.MarketKeeper.GetSwapDecCoin(input.Ctx, offerCoin, core.MicroLunaDenom) + require.NoError(t, err) + require.Equal(t, offerCoin.Amount.Quo(lunaPriceInSDR), retCoin.Amount) + } + + offerCoin := sdk.NewDecCoin(core.MicroSDRDenom, lunaPriceInSDR.QuoInt64(2).TruncateInt()) + _, err := input.MarketKeeper.GetSwapDecCoin(input.Ctx, offerCoin, core.MicroLunaDenom) + require.Error(t, err) +} diff --git a/x/market/internal/keeper/params.go b/x/market/internal/keeper/params.go new file mode 100644 index 000000000..2cfa5b89f --- /dev/null +++ b/x/market/internal/keeper/params.go @@ -0,0 +1,41 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/terra-project/core/x/market/internal/types" +) + +// ParamTable for market module +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&types.Params{}) +} + +// DailyLunaDeltaCap +func (k Keeper) DailyLunaDeltaCap(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyDailyLunaDeltaCap, &res) + return +} + +// MinSwapSpread +func (k Keeper) MinSwapSpread(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyMinSwapSpread, &res) + return +} + +// MaxSwapSpread +func (k Keeper) MaxSwapSpread(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyMaxSwapSpread, &res) + return +} + +// GetParams returns the total set of market parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of market parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/market/querier.go b/x/market/internal/keeper/querier.go similarity index 60% rename from x/market/querier.go rename to x/market/internal/keeper/querier.go index d81d873e5..7095e3b7e 100644 --- a/x/market/querier.go +++ b/x/market/internal/keeper/querier.go @@ -1,57 +1,41 @@ -package market +package keeper import ( "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" -) -// query endpoints supported by the oracle Querier -const ( - QuerySwap = "swap" - QueryParams = "params" + "github.com/terra-project/core/x/market/internal/types" ) // NewQuerier is the module level router for state queries func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - case QuerySwap: - return querySwap(ctx, path[1:], req, keeper) - case QueryParams: - return queryParams(ctx, req, keeper) + case types.QuerySwap: + return querySwap(ctx, req, keeper) + case types.QueryPrevDayIssuance: + return queryPrevDayIssuance(ctx, req, keeper) + case types.QueryParameters: + return queryParameters(ctx, keeper) default: return nil, sdk.ErrUnknownRequest("unknown market query endpoint") } } } -// QuerySwapParams for query 'custom/market/swap' -type QuerySwapParams struct { - OfferCoin sdk.Coin -} - -func NewQuerySwapParams(offerCoin sdk.Coin) QuerySwapParams { - return QuerySwapParams{ - OfferCoin: offerCoin, - } -} - -func querySwap(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - askDenom := path[0] - - var params QuerySwapParams +func querySwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QuerySwapParams err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } - if askDenom == params.OfferCoin.Denom { - return nil, ErrRecursiveSwap(DefaultCodespace, askDenom) + if params.AskDenom == params.OfferCoin.Denom { + return nil, types.ErrRecursiveSwap(types.DefaultCodespace, params.AskDenom) } - swapCoin, spread, err := keeper.GetSwapCoin(ctx, params.OfferCoin, askDenom, false) + swapCoin, spread, err := keeper.GetSwapCoin(ctx, params.OfferCoin, params.AskDenom, false) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("Failed to get swapped coin amount", err.Error())) } @@ -72,7 +56,16 @@ func querySwap(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Kee return bz, nil } -func queryParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryPrevDayIssuance(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetPrevDayIssuance(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryParameters(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) diff --git a/x/market/internal/keeper/querier_test.go b/x/market/internal/keeper/querier_test.go new file mode 100644 index 000000000..0eec436d9 --- /dev/null +++ b/x/market/internal/keeper/querier_test.go @@ -0,0 +1,69 @@ +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/types" +) + +func TestNewQuerier(t *testing.T) { + input := CreateTestInput(t) + + querier := NewQuerier(input.MarketKeeper) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + _, err := querier(input.Ctx, []string{types.QueryParameters}, query) + require.NoError(t, err) +} + +func TestQueryParams(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + + var params types.Params + + res, errRes := queryParameters(input.Ctx, input.MarketKeeper) + require.NoError(t, errRes) + + err := cdc.UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + require.Equal(t, input.MarketKeeper.GetParams(input.Ctx), params) +} + +func TestQuerySwap(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + + price := sdk.NewDecWithPrec(17, 1) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, price) + + offerCoin := sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(10)) + queryParams := types.NewQuerySwapParams(offerCoin, core.MicroSDRDenom) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querySwap(input.Ctx, req, input.MarketKeeper) + require.NoError(t, err) + + var swapCoin sdk.Coin + err = cdc.UnmarshalJSON(res, &swapCoin) + require.NoError(t, err) + require.Equal(t, core.MicroSDRDenom, swapCoin.Denom) + require.Equal(t, sdk.NewInt(17), swapCoin.Amount) +} diff --git a/x/market/internal/keeper/test_utils.go b/x/market/internal/keeper/test_utils.go new file mode 100644 index 000000000..586088516 --- /dev/null +++ b/x/market/internal/keeper/test_utils.go @@ -0,0 +1,179 @@ +// nolint:deadcode unused noalias +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/types" + "github.com/terra-project/core/x/oracle" + + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +var ( + PubKeys = []crypto.PubKey{ + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + } + + Addrs = []sdk.AccAddress{ + sdk.AccAddress(PubKeys[0].Address()), + sdk.AccAddress(PubKeys[1].Address()), + sdk.AccAddress(PubKeys[2].Address()), + } + + ValAddrs = []sdk.ValAddress{ + sdk.ValAddress(PubKeys[0].Address()), + sdk.ValAddress(PubKeys[1].Address()), + sdk.ValAddress(PubKeys[2].Address()), + } + + InitTokens = sdk.TokensFromConsensusPower(200) + InitCoins = sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens)) +) + +// TestInput nolint +type TestInput struct { + Ctx sdk.Context + Cdc *codec.Codec + OracleKeeper oracle.Keeper + SupplyKeeper supply.Keeper + MarketKeeper Keeper +} + +func newTestCodec() *codec.Codec { + cdc := codec.New() + + oracle.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + supply.RegisterCodec(cdc) + staking.RegisterCodec(cdc) + distr.RegisterCodec(cdc) + params.RegisterCodec(cdc) + + return cdc +} + +// CreateTestInput nolint +func CreateTestInput(t *testing.T) TestInput { + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) + keyDistr := sdk.NewKVStoreKey(distr.StoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + keyMarket := sdk.NewKVStoreKey(types.StoreKey) + + cdc := newTestCodec() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) + + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) + + require.NoError(t, ms.LoadLatestVersion()) + + blackListAddrs := map[string]bool{ + auth.FeeCollectorName: true, + staking.NotBondedPoolName: true, + staking.BondedPoolName: true, + distr.ModuleName: true, + types.ModuleName: true, + } + + paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams, params.DefaultCodespace) + accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) + bankKeeper := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blackListAddrs) + + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + distr.ModuleName: nil, + types.ModuleName: {supply.Burner, supply.Minter}, + } + + supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) + totalSupply := sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens.MulRaw(int64(len(Addrs))))) + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + stakingKeeper := staking.NewKeeper( + cdc, + keyStaking, tKeyStaking, + supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), + staking.DefaultCodespace, + ) + + distrKeeper := distr.NewKeeper( + cdc, + keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), + stakingKeeper, supplyKeeper, distr.DefaultCodespace, + auth.FeeCollectorName, blackListAddrs, + ) + + oracleKeeper := oracle.NewKeeper( + cdc, + keyOracle, paramsKeeper.Subspace(oracle.DefaultParamspace), + distrKeeper, stakingKeeper, supplyKeeper, distr.ModuleName, + oracle.DefaultCodespace, + ) + + keeper := NewKeeper( + cdc, + keyMarket, paramsKeeper.Subspace(types.DefaultParamspace), + oracleKeeper, supplyKeeper, types.DefaultCodespace, + ) + + keeper.SetParams(ctx, types.DefaultParams()) + + feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) + distrAcc := supply.NewEmptyModuleAccount(distr.ModuleName) + marketAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner, supply.Minter) + + supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) + supplyKeeper.SetModuleAccount(ctx, bondPool) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + supplyKeeper.SetModuleAccount(ctx, distrAcc) + supplyKeeper.SetModuleAccount(ctx, marketAcc) + + for _, addr := range Addrs { + _, err := bankKeeper.AddCoins(ctx, sdk.AccAddress(addr), InitCoins) + require.NoError(t, err) + } + + return TestInput{ctx, cdc, oracleKeeper, supplyKeeper, keeper} +} diff --git a/x/market/codec.go b/x/market/internal/types/codec.go similarity index 69% rename from x/market/codec.go rename to x/market/internal/types/codec.go index 991df56de..8ef0d6ea2 100644 --- a/x/market/codec.go +++ b/x/market/internal/types/codec.go @@ -1,10 +1,11 @@ -package market +package types import ( "github.com/cosmos/cosmos-sdk/codec" ) -var msgCdc = codec.New() +// Internal Module Codec +var ModuleCdc = codec.New() // RegisterCodec concretes types on codec codec func RegisterCodec(cdc *codec.Codec) { @@ -12,5 +13,5 @@ func RegisterCodec(cdc *codec.Codec) { } func init() { - RegisterCodec(msgCdc) + RegisterCodec(ModuleCdc) } diff --git a/x/market/errors.go b/x/market/internal/types/errors.go similarity index 87% rename from x/market/errors.go rename to x/market/internal/types/errors.go index 2632790ea..157444ce3 100644 --- a/x/market/errors.go +++ b/x/market/internal/types/errors.go @@ -1,17 +1,19 @@ -package market +package types import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +type codeType = sdk.CodeType + // market error codes const ( DefaultCodespace sdk.CodespaceType = "market" - CodeInsufficientSwap sdk.CodeType = 1 - CodeNoEffectivePrice sdk.CodeType = 2 - CodeRecursiveSwap sdk.CodeType = 3 - CodeExceedsSwapLimit sdk.CodeType = 4 + CodeInsufficientSwap codeType = 1 + CodeNoEffectivePrice codeType = 2 + CodeRecursiveSwap codeType = 3 + CodeExceedsSwapLimit codeType = 4 ) // ---------------------------------------- diff --git a/x/market/internal/types/events.go b/x/market/internal/types/events.go new file mode 100644 index 000000000..0e0fea371 --- /dev/null +++ b/x/market/internal/types/events.go @@ -0,0 +1,16 @@ +// noalias +package types + +// Market module event types +const ( + EventSwap = "swap" + EventDaliyIssuanceUpdate = "daliy_issuance_update" + + AttributeKeyOffer = "offer" + AttributeKeyTrader = "trader" + AttributeKeySwapCoin = "swap_coin" + AttributeKeySwapFee = "swap_fee" + AttributeKeyIssuance = "issuance" + + AttributeValueCategory = ModuleName +) diff --git a/x/market/internal/types/expected_keepers.go b/x/market/internal/types/expected_keepers.go new file mode 100644 index 000000000..e382ed254 --- /dev/null +++ b/x/market/internal/types/expected_keepers.go @@ -0,0 +1,23 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// expected supply keeper +type SupplyKeeper interface { + GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI + GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error +} + +// expected oracle keeper +type OracleKeeper interface { + GetLunaPrice(ctx sdk.Context, denom string) (price sdk.Dec, err sdk.Error) +} diff --git a/x/market/internal/types/genesis.go b/x/market/internal/types/genesis.go new file mode 100644 index 000000000..4fbe81e44 --- /dev/null +++ b/x/market/internal/types/genesis.go @@ -0,0 +1,41 @@ +package types + +import "bytes" + +// GenesisState - all market state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` // market params +} + +// NewGenesisState creates a new GenesisState object +func NewGenesisState(params Params) GenesisState { + return GenesisState{ + Params: params, + } +} + +// get raw genesis raw message for testing +func DefaultGenesisState() GenesisState { + return GenesisState{ + Params: DefaultParams(), + } +} + +// ValidateGenesis validates the provided oracle genesis state to ensure the +// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) +func ValidateGenesis(data GenesisState) error { + return data.Params.Validate() +} + +// Checks whether 2 GenesisState structs are equivalent. +func (data GenesisState) Equal(data2 GenesisState) bool { + b1 := ModuleCdc.MustMarshalBinaryBare(data) + b2 := ModuleCdc.MustMarshalBinaryBare(data2) + return bytes.Equal(b1, b2) +} + +// Returns if a GenesisState is empty or has data in it +func (data GenesisState) IsEmpty() bool { + emptyGenState := GenesisState{} + return data.Equal(emptyGenState) +} diff --git a/x/market/internal/types/genesis_test.go b/x/market/internal/types/genesis_test.go new file mode 100644 index 000000000..06d989ca5 --- /dev/null +++ b/x/market/internal/types/genesis_test.go @@ -0,0 +1,37 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGenesisValidation(t *testing.T) { + genState := DefaultGenesisState() + require.NoError(t, ValidateGenesis(genState)) + + genState.Params.DailyLunaDeltaCap = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) + + genState.Params.DailyLunaDeltaCap = sdk.OneDec() + genState.Params.MaxSwapSpread = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) + + genState.Params.MaxSwapSpread = sdk.ZeroDec() + genState.Params.MinSwapSpread = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) +} + +func TestGenesisEqual(t *testing.T) { + genState1 := DefaultGenesisState() + genState2 := DefaultGenesisState() + + require.True(t, genState1.Equal(genState2)) +} + +func TestGenesisEmpty(t *testing.T) { + genState := GenesisState{} + require.True(t, genState.IsEmpty()) +} diff --git a/x/market/internal/types/keys.go b/x/market/internal/types/keys.go new file mode 100644 index 000000000..beaef0aa3 --- /dev/null +++ b/x/market/internal/types/keys.go @@ -0,0 +1,24 @@ +package types + +const ( + // ModuleName is the name of the market module + ModuleName = "market" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // RouterKey is the msg router key for the market module + RouterKey = ModuleName + + // QuerierRoute is the query router key for the market module + QuerierRoute = ModuleName +) + +// Keys for market store +// Items are stored with the following key: values +// +// - 0x01: sdk.Int +var ( + //Keys for store prefixed + PrevDayIssuanceKey = []byte{0x01} // key for prev day issuance +) diff --git a/x/market/msg.go b/x/market/internal/types/msgs.go similarity index 74% rename from x/market/msg.go rename to x/market/internal/types/msgs.go index 312440872..885b198f4 100644 --- a/x/market/msg.go +++ b/x/market/internal/types/msgs.go @@ -1,4 +1,4 @@ -package market +package types import ( "fmt" @@ -6,14 +6,19 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// ensure Msg interface compliance at compile time +var ( + _ sdk.Msg = &MsgSwap{} +) + //-------------------------------------------------------- //-------------------------------------------------------- // MsgSwap contains a swap request type MsgSwap struct { - Trader sdk.AccAddress `json:"trader"` // Address of the trader - OfferCoin sdk.Coin `json:"offer_coin"` // Coin being offered - AskDenom string `json:"ask_denom"` // Denom of the coin to swap to + Trader sdk.AccAddress `json:"trader" yaml:"trader"` // Address of the trader + OfferCoin sdk.Coin `json:"offer_coin" yaml:"offer_coin"` // Coin being offered + AskDenom string `json:"ask_denom" yaml:"ask_denom"` // Denom of the coin to swap to } // NewMsgSwap creates a MsgSwap instance @@ -33,7 +38,7 @@ func (msg MsgSwap) Type() string { return "swap" } // GetSignBytes Implements Msg func (msg MsgSwap) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } // GetSigners Implements Msg @@ -47,7 +52,7 @@ func (msg MsgSwap) ValidateBasic() sdk.Error { return sdk.ErrInvalidAddress("Invalid address: " + msg.Trader.String()) } - if msg.OfferCoin.Amount.LT(sdk.ZeroInt()) { + if msg.OfferCoin.Amount.LTE(sdk.ZeroInt()) { return ErrInsufficientSwapCoins(DefaultCodespace, msg.OfferCoin.Amount) } diff --git a/x/market/internal/types/msgs_test.go b/x/market/internal/types/msgs_test.go new file mode 100644 index 000000000..d1cc8ba52 --- /dev/null +++ b/x/market/internal/types/msgs_test.go @@ -0,0 +1,36 @@ +package types + +import ( + "testing" + + core "github.com/terra-project/core/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/stretchr/testify/require" +) + +func TestMsgSwap(t *testing.T) { + _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) + + tests := []struct { + trader sdk.AccAddress + offerCoin sdk.Coin + askDenom string + expectPass bool + }{ + {addrs[0], sdk.NewCoin(core.MicroLunaDenom, sdk.OneInt()), core.MicroSDRDenom, true}, + {sdk.AccAddress{}, sdk.NewCoin(core.MicroLunaDenom, sdk.OneInt()), core.MicroSDRDenom, false}, + {addrs[0], sdk.NewCoin(core.MicroLunaDenom, sdk.ZeroInt()), core.MicroSDRDenom, false}, + {addrs[0], sdk.NewCoin(core.MicroLunaDenom, sdk.OneInt()), core.MicroLunaDenom, false}, + } + + for i, tc := range tests { + msg := NewMsgSwap(tc.trader, tc.offerCoin, tc.askDenom) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + } + } +} diff --git a/x/market/internal/types/params.go b/x/market/internal/types/params.go new file mode 100644 index 000000000..26f3e77e0 --- /dev/null +++ b/x/market/internal/types/params.go @@ -0,0 +1,78 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/subspace" +) + +// DefaultParamspace +const DefaultParamspace = ModuleName + +// Parameter keys +var ( + ParamStoreKeyDailyLunaDeltaCap = []byte("dailylunadeltalimit") + ParamStoreKeyMaxSwapSpread = []byte("maxswapspread") + ParamStoreKeyMinSwapSpread = []byte("minswapspread") +) + +// Default parameter values +var ( + DefaultDailyLunaDeltaCap = sdk.NewDecWithPrec(5, 3) // 0.5% + DefaultMaxSwapSpread = sdk.NewDec(1) // 100% + DefaultMinSwapSpread = sdk.NewDecWithPrec(2, 2) // 2% +) + +var _ subspace.ParamSet = &Params{} + +// Params market parameters +type Params struct { + DailyLunaDeltaCap sdk.Dec `json:"daily_luna_delta_cap" yaml:"daily_luna_delta_cap"` + MaxSwapSpread sdk.Dec `json:"max_swap_spread" yaml:"max_swap_spread"` + MinSwapSpread sdk.Dec `json:"min_swap_spread" yaml:"min_swap_spread"` +} + +// DefaultParams creates default market module parameters +func DefaultParams() Params { + return Params{ + DailyLunaDeltaCap: DefaultDailyLunaDeltaCap, + MaxSwapSpread: DefaultMaxSwapSpread, + MinSwapSpread: DefaultMinSwapSpread, + } +} + +// Validate a set of params +func (params Params) Validate() error { + if params.DailyLunaDeltaCap.IsNegative() { + return fmt.Errorf("market daily luna issuance change should be non-negative, is %s", params.DailyLunaDeltaCap.String()) + } + if params.MinSwapSpread.IsNegative() || params.MinSwapSpread.GT(sdk.OneDec()) { + return fmt.Errorf("market minimum swap spead should be non-negative, is %s", params.MinSwapSpread.String()) + } + if params.MaxSwapSpread.LT(params.MinSwapSpread) || params.MaxSwapSpread.GT(sdk.OneDec()) { + return fmt.Errorf("market maximum swap spead should be larger or equal to the minimum, is %s", params.MaxSwapSpread.String()) + } + + return nil +} + +// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs +// pairs of oracle module's parameters. +// nolint +func (params *Params) ParamSetPairs() subspace.ParamSetPairs { + return subspace.ParamSetPairs{ + {Key: ParamStoreKeyDailyLunaDeltaCap, Value: ¶ms.DailyLunaDeltaCap}, + {Key: ParamStoreKeyMaxSwapSpread, Value: ¶ms.MaxSwapSpread}, + {Key: ParamStoreKeyMinSwapSpread, Value: ¶ms.MinSwapSpread}, + } +} + +// implements fmt.Stringer +func (params Params) String() string { + return fmt.Sprintf(`Treasury Params: + DailyLunaDeltaCap: %s + MaxSwapSpread: %s + MinSwapSpread: %s + `, params.DailyLunaDeltaCap, params.MaxSwapSpread, params.MinSwapSpread) +} diff --git a/x/market/internal/types/querier.go b/x/market/internal/types/querier.go new file mode 100644 index 000000000..99164c801 --- /dev/null +++ b/x/market/internal/types/querier.go @@ -0,0 +1,26 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// query endpoints supported by the oracle Querier +const ( + QuerySwap = "swap" + QueryPrevDayIssuance = "lastDayIssuance" + QueryParameters = "parameters" +) + +// QuerySwapParams for query +// - 'custom/market/swap' +type QuerySwapParams struct { + OfferCoin sdk.Coin + AskDenom string +} + +func NewQuerySwapParams(offerCoin sdk.Coin, askDenom string) QuerySwapParams { + return QuerySwapParams{ + OfferCoin: offerCoin, + AskDenom: askDenom, + } +} diff --git a/x/market/keeper.go b/x/market/keeper.go deleted file mode 100644 index f568b2e62..000000000 --- a/x/market/keeper.go +++ /dev/null @@ -1,138 +0,0 @@ -package market - -import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper holds data structures for the market module -type Keeper struct { - cdc *codec.Codec // Codec to encore/decode structs - key sdk.StoreKey // Key to our module's store - ok OracleKeeper - mk MintKeeper - paramSpace params.Subspace -} - -// NewKeeper creates a new Keeper for the market module -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ok OracleKeeper, mk MintKeeper, paramspace params.Subspace) Keeper { - return Keeper{ - cdc: cdc, - key: key, - ok: ok, - mk: mk, - paramSpace: paramspace.WithKeyTable(paramKeyTable()), - } -} - -// ComputeLunaDelta returns the issuance rate change of Luna for the day post-swap -func (k Keeper) ComputeLunaDelta(ctx sdk.Context, change sdk.Int) sdk.Dec { - curDay := ctx.BlockHeight() / util.BlocksPerDay - - // Start limits on day 2 - if curDay == 0 { - return sdk.ZeroDec() - } - - prevIssuance := k.mk.GetIssuance(ctx, assets.MicroLunaDenom, sdk.NewInt(curDay-1)) - if !prevIssuance.IsZero() { - curIssuance := k.mk.GetIssuance(ctx, assets.MicroLunaDenom, sdk.NewInt(curDay)) - postSwapIssuance := curIssuance.Add(change) - - return sdk.NewDecFromInt(postSwapIssuance.Sub(prevIssuance)).QuoInt(prevIssuance) - } - - return sdk.ZeroDec() -} - -// GetSwapCoin returns the amount of asked coins should be returned for a given offerCoin at the effective -// exchange rate registered with the oracle. -// Returns an Error if the swap is recursive, or the coins to be traded are unknown by the oracle, or the amount -// to trade is too small. -// Ignores caps and spreads if isInternal = true. -func (k Keeper) GetSwapCoin(ctx sdk.Context, offerCoin sdk.Coin, askDenom string, isInternal bool) (retCoin sdk.Coin, spread sdk.Dec, err sdk.Error) { - params := k.GetParams(ctx) - - offerRate, err := k.ok.GetLunaSwapRate(ctx, offerCoin.Denom) - if err != nil { - return sdk.Coin{}, sdk.ZeroDec(), ErrNoEffectivePrice(DefaultCodespace, offerCoin.Denom) - } - - askRate, err := k.ok.GetLunaSwapRate(ctx, askDenom) - if err != nil { - return sdk.Coin{}, sdk.ZeroDec(), ErrNoEffectivePrice(DefaultCodespace, askDenom) - } - - retAmount := sdk.NewDecFromInt(offerCoin.Amount).Mul(askRate).Quo(offerRate).TruncateInt() - if retAmount.Equal(sdk.ZeroInt()) { - return sdk.Coin{}, sdk.ZeroDec(), ErrInsufficientSwapCoins(DefaultCodespace, offerCoin.Amount) - } - - // We only charge spread for NON-INTERNAL swaps involving luna; if not, just pass. - if isInternal || (offerCoin.Denom != assets.MicroLunaDenom && askDenom != assets.MicroLunaDenom) { - return sdk.NewCoin(askDenom, retAmount), sdk.ZeroDec(), nil - } - - dailyDelta := sdk.ZeroDec() - if offerCoin.Denom == assets.MicroLunaDenom { - dailyDelta = k.ComputeLunaDelta(ctx, offerCoin.Amount.Neg()) - } else if askDenom == assets.MicroLunaDenom { - dailyDelta = k.ComputeLunaDelta(ctx, retAmount) - } - - // delta should be positive to apply spread - dailyDelta = dailyDelta.Abs() - - // Do not allow swaps beyond the daily cap - maxDelta := params.DailyLunaDeltaCap - if dailyDelta.GT(maxDelta) { - return sdk.Coin{}, sdk.ZeroDec(), ErrExceedsDailySwapLimit(DefaultCodespace) - } - - // Compute a spread, which is initialiy MinSwapSpread and grows linearly to MaxSwapSpread with delta - spread = params.MinSwapSpread.Add(dailyDelta.Quo(maxDelta).Mul(params.MaxSwapSpread.Sub(params.MinSwapSpread))) - - return sdk.NewCoin(askDenom, retAmount), spread, nil -} - -// GetSwapDecCoin returns the amount of asked DecCoins should be returned for a given offerCoin at the effective -// exchange rate registered with the oracle. -// Different from swapcoins, SwapDecCoins does not charge a spread as its use is system internal. -// Similar to SwapCoins, but operates over sdk.DecCoins for convenience and accuracy. -func (k Keeper) GetSwapDecCoin(ctx sdk.Context, offerCoin sdk.DecCoin, askDenom string) (sdk.DecCoin, sdk.Error) { - offerRate, err := k.ok.GetLunaSwapRate(ctx, offerCoin.Denom) - if err != nil { - return sdk.DecCoin{}, ErrNoEffectivePrice(DefaultCodespace, offerCoin.Denom) - } - - askRate, err := k.ok.GetLunaSwapRate(ctx, askDenom) - if err != nil { - return sdk.DecCoin{}, ErrNoEffectivePrice(DefaultCodespace, askDenom) - } - - retAmount := offerCoin.Amount.Mul(askRate).Quo(offerRate) - if retAmount.LTE(sdk.ZeroDec()) { - return sdk.DecCoin{}, ErrInsufficientSwapCoins(DefaultCodespace, offerCoin.Amount.TruncateInt()) - } - - return sdk.NewDecCoinFromDec(askDenom, retAmount), nil -} - -//----------------------------------- -// Params logic - -// GetParams get budget params from the global param store -func (k Keeper) GetParams(ctx sdk.Context) Params { - var resultParams Params - k.paramSpace.Get(ctx, paramStoreKeyParams, &resultParams) - return resultParams -} - -// SetParams set budget params from the global param store -func (k Keeper) SetParams(ctx sdk.Context, params Params) { - k.paramSpace.Set(ctx, paramStoreKeyParams, ¶ms) -} diff --git a/x/market/keeper_keys.go b/x/market/keeper_keys.go deleted file mode 100644 index 4ac1f7e39..000000000 --- a/x/market/keeper_keys.go +++ /dev/null @@ -1,15 +0,0 @@ -package market - -import ( - "github.com/cosmos/cosmos-sdk/x/params" -) - -var ( - paramStoreKeyParams = []byte("params") -) - -func paramKeyTable() params.KeyTable { - return params.NewKeyTable( - paramStoreKeyParams, Params{}, - ) -} diff --git a/x/market/keeper_test.go b/x/market/keeper_test.go deleted file mode 100644 index 17f6ec477..000000000 --- a/x/market/keeper_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package market - -import ( - "math" - "testing" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" -) - -func TestKeeperSwapCoinsBasic(t *testing.T) { - - input := createTestInput(t) - - lnasdrRate := sdk.NewDec(4) - lnacnyRate := sdk.NewDec(8) - offerCoin := sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(2).MulRaw(assets.MicroUnit)) - askCoin := sdk.NewCoin(assets.MicroCNYDenom, sdk.NewInt(4).MulRaw(assets.MicroUnit)) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, offerCoin.Denom, lnasdrRate) - input.oracleKeeper.SetLunaSwapRate(input.ctx, askCoin.Denom, lnacnyRate) - - retCoin, spread, err := input.marketKeeper.GetSwapCoin(input.ctx, offerCoin, askCoin.Denom, false) - require.Nil(t, err) - require.Zero(t, spread.TruncateInt64(), "Spread should be 0 for non luna swaps") - - require.Equal(t, retCoin, askCoin) -} - -func TestKeeperSwapCoinsLunaCap(t *testing.T) { - - input := createTestInput(t) - - // Set params - params := DefaultParams() - input.marketKeeper.SetParams(input.ctx, params) - - baseAmount := sdk.NewInt(int64(math.Pow10(9))) - // Set day to 2 and issuance as the same as the day before - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, baseAmount)) - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerDay + 1) - - // Set exchange rate. Keep it at 1:1 for simplicity - lnasdrRate := sdk.NewDec(1) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, lnasdrRate) - - maxDelta := params.DailyLunaDeltaCap.MulInt(baseAmount).TruncateInt() - - // Check cap luna -> sdr swap, at the cap. Should succeed - offerCoin := sdk.NewCoin(assets.MicroLunaDenom, maxDelta) - _, spread, err := input.marketKeeper.GetSwapCoin(input.ctx, offerCoin, assets.MicroSDRDenom, false) - require.Nil(t, err) - require.Equal(t, params.MaxSwapSpread, spread) - - // Check cap luna -> sdr swap, 1 coin higher than the cap. Should fail - offerCoin = sdk.NewCoin(assets.MicroLunaDenom, maxDelta.Add(sdk.OneInt())) - _, _, err = input.marketKeeper.GetSwapCoin(input.ctx, offerCoin, assets.MicroSDRDenom, false) - require.NotNil(t, err) - - // Check cap sdr -> luna swap,at the cap. Should succeed - offerCoin = sdk.NewCoin(assets.MicroSDRDenom, maxDelta) - _, spread, err = input.marketKeeper.GetSwapCoin(input.ctx, offerCoin, assets.MicroLunaDenom, false) - require.Nil(t, err) - require.Equal(t, params.MaxSwapSpread, spread) - - // Check cap sdr -> luna swap, 1 coin higher than the cap. Should fail - offerCoin = sdk.NewCoin(assets.MicroSDRDenom, maxDelta.Add(sdk.OneInt())) - _, _, err = input.marketKeeper.GetSwapCoin(input.ctx, offerCoin, assets.MicroLunaDenom, false) - require.NotNil(t, err) -} - -func TestKeeperSwapDecCoins(t *testing.T) { - input := createTestInput(t) - - lnasdrRate := sdk.NewDec(4) - lnacnyRate := sdk.NewDec(8) - offerCoin := sdk.NewDecCoin(assets.MicroSDRDenom, sdk.NewInt(2).MulRaw(assets.MicroUnit)) - askCoin := sdk.NewDecCoin(assets.MicroCNYDenom, sdk.NewInt(4).MulRaw(assets.MicroUnit)) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, offerCoin.Denom, lnasdrRate) - input.oracleKeeper.SetLunaSwapRate(input.ctx, askCoin.Denom, lnacnyRate) - - retCoin, err := input.marketKeeper.GetSwapDecCoin(input.ctx, offerCoin, askCoin.Denom) - require.Nil(t, err) - - require.Equal(t, retCoin, askCoin) -} diff --git a/x/market/log.go b/x/market/log.go deleted file mode 100644 index 778a68839..000000000 --- a/x/market/log.go +++ /dev/null @@ -1,30 +0,0 @@ -package market - -import ( - "encoding/json" -) - -const ( - // LogKeySwapCoin is the amount of swapped coin - LogKeySwapCoin = string("swap_coin") - // LogKeySwapFee is the fee for swap operation - LogKeySwapFee = string("swap_fee") -) - -// Log is map type object to organize msg result -type Log map[string]string - -func NewLog() Log { - return Log{} -} - -func (log Log) append(key, value string) Log { - log[key] = value - - return log -} - -func (log Log) String() string { - jsonMap, _ := json.Marshal(log) - return string(jsonMap) -} diff --git a/x/market/module.go b/x/market/module.go new file mode 100644 index 000000000..044372c10 --- /dev/null +++ b/x/market/module.go @@ -0,0 +1,135 @@ +package market + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/exported" + + "github.com/terra-project/core/x/market/client/cli" + "github.com/terra-project/core/x/market/client/rest" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return ModuleName +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(StoreKey, cdc) +} + +// extra function from sdk.AppModuleBasic +// iterate the genesis accounts and perform an operation at each of them +// - to used by other modules +func (AppModuleBasic) IterateGenesisAccounts(cdc *codec.Codec, appGenesis map[string]json.RawMessage, iterateFn func(exported.Account) (stop bool)) { +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + } +} + +// module name +func (AppModule) Name() string { return ModuleName } + +// register invariants +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// module message route name +func (AppModule) Route() string { return RouterKey } + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// module querier route name +func (AppModule) QuerierRoute() string { return RouterKey } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, genesisState) + return []abci.ValidatorUpdate{} +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + genesisState := ExportGenesis(ctx, am.keeper) + data := ModuleCdc.MustMarshalJSON(genesisState) + return data +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + EndBlocker(ctx, am.keeper) + return []abci.ValidatorUpdate{} +} diff --git a/x/market/msg_test.go b/x/market/msg_test.go deleted file mode 100644 index a3e14f220..000000000 --- a/x/market/msg_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package market - -import ( - "github.com/terra-project/core/types/assets" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" - "github.com/stretchr/testify/require" -) - -func TestMsgPriceFeed(t *testing.T) { - _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) - tests := []struct { - offerCoin sdk.Coin - askDenom string - expectPass bool - }{ - {sdk.NewInt64Coin(assets.MicroKRWDenom, sdk.NewInt(10).MulRaw(assets.MicroUnit).Int64()), assets.MicroLunaDenom, true}, - {sdk.NewInt64Coin(assets.MicroLunaDenom, sdk.NewInt(10).MulRaw(assets.MicroUnit).Int64()), assets.MicroUSDDenom, true}, - {sdk.NewInt64Coin(assets.MicroUSDDenom, sdk.NewInt(10).MulRaw(assets.MicroUnit).Int64()), assets.MicroUSDDenom, false}, - } - - for i, tc := range tests { - msg := NewMsgSwap(addrs[0], tc.offerCoin, tc.askDenom) - if tc.expectPass { - require.Nil(t, msg.ValidateBasic(), "test: %v", i) - } else { - require.NotNil(t, msg.ValidateBasic(), "test: %v", i) - } - } -} diff --git a/x/market/params.go b/x/market/params.go deleted file mode 100644 index facfae6f0..000000000 --- a/x/market/params.go +++ /dev/null @@ -1,54 +0,0 @@ -package market - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Params market parameters -type Params struct { - DailyLunaDeltaCap sdk.Dec `json:"daily_luna_delta_limit"` // daily % inflation or deflation cap on Luna - MinSwapSpread sdk.Dec `json:"min_swap_spread"` // minimum spread for swaps involving Luna - MaxSwapSpread sdk.Dec `json:"max_swap_spread"` // maximum spread for swaps involving Luna -} - -// NewParams creates a new param instance -func NewParams(dailyLunaDeltaCap, minSwapSpread, maxSwapSpread sdk.Dec) Params { - return Params{ - DailyLunaDeltaCap: dailyLunaDeltaCap, - MinSwapSpread: minSwapSpread, - MaxSwapSpread: maxSwapSpread, - } -} - -// DefaultParams creates default market module parameters -func DefaultParams() Params { - return NewParams( - sdk.NewDecWithPrec(5, 3), // 0.5% - sdk.NewDecWithPrec(2, 2), // 2% - sdk.NewDecWithPrec(10, 1), // 10% - ) -} - -func validateParams(params Params) error { - if params.DailyLunaDeltaCap.IsNegative() { - return fmt.Errorf("market daily luna issuance change should be non-negative, is %s", params.DailyLunaDeltaCap.String()) - } - if params.MinSwapSpread.IsNegative() { - return fmt.Errorf("market minimum swap spead should be non-negative, is %s", params.MinSwapSpread.String()) - } - if params.MaxSwapSpread.LT(params.MinSwapSpread) { - return fmt.Errorf("market maximum swap spead should be larger or equal to the minimum, is %s", params.MaxSwapSpread.String()) - } - - return nil -} - -func (params Params) String() string { - return fmt.Sprintf(`market Params: - DailyLunaDeltaCap: %v, - MinSwapSpread: %v, - MaxSwapSpread: %v - `, params.DailyLunaDeltaCap, params.MinSwapSpread, params.MaxSwapSpread) -} diff --git a/x/market/simulation/msgs.go b/x/market/simulation/msgs.go new file mode 100644 index 000000000..f05dc1afa --- /dev/null +++ b/x/market/simulation/msgs.go @@ -0,0 +1,34 @@ +package simulation + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/simulation" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market" +) + +// SimulateMsgPrevote generates a MsgPrevote with random values +func SimulateMsgPrevote(k market.Keeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + acc := simulation.RandomAcc(r, accs) + + msg := market.NewMsgSwap(acc.Address, sdk.NewInt64Coin(core.MicroLunaDenom, rand.Int63()), core.MicroSDRDenom) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(market.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + ctx, write := ctx.CacheContext() + ok := market.NewHandler(k)(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} diff --git a/x/market/spread.go b/x/market/spread.go deleted file mode 100644 index 0274b033b..000000000 --- a/x/market/spread.go +++ /dev/null @@ -1 +0,0 @@ -package market diff --git a/x/market/tags/tags.go b/x/market/tags/tags.go deleted file mode 100644 index 9ae267a50..000000000 --- a/x/market/tags/tags.go +++ /dev/null @@ -1,7 +0,0 @@ -package tags - -// Market tags -var ( - Offer = "offer" - Trader = "trader" -) diff --git a/x/market/test_common.go b/x/market/test_common.go deleted file mode 100644 index 42293653d..000000000 --- a/x/market/test_common.go +++ /dev/null @@ -1,157 +0,0 @@ -package market - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - addrs = []sdk.AccAddress{ - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - } - - uSDRAmt = sdk.NewInt(1005).MulRaw(assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - accKeeper auth.AccountKeeper - bankKeeper bank.Keeper - marketKeeper Keeper - oracleKeeper oracle.Keeper - mintKeeper mint.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyMarket := sdk.NewKVStoreKey(StoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingKeeper.SetParams(ctx, staking.DefaultParams()) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := NewKeeper( - cdc, - keyMarket, - oracleKeeper, - mintKeeper, - paramsKeeper.Subspace(DefaultParamspace), - ) - - marketKeeper.SetParams(ctx, DefaultParams()) - - for _, addr := range addrs { - err := mintKeeper.Mint(ctx, addr, sdk.NewCoin(assets.MicroSDRDenom, uSDRAmt)) - require.NoError(t, err) - } - - return testInput{ctx, accKeeper, bankKeeper, marketKeeper, oracleKeeper, mintKeeper} -} diff --git a/x/market/test_utils.go b/x/market/test_utils.go new file mode 100644 index 000000000..32ffb975e --- /dev/null +++ b/x/market/test_utils.go @@ -0,0 +1,28 @@ +// nolint:deadcode unused DONTCOVER +package market + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market/internal/keeper" +) + +var ( + uSDRAmt = sdk.NewInt(1005 * core.MicroUnit) + stakingAmt = sdk.TokensFromConsensusPower(10) + + randomPrice = sdk.NewDec(1700) +) + +func setup(t *testing.T) (keeper.TestInput, sdk.Handler) { + input := keeper.CreateTestInput(t) + params := input.MarketKeeper.GetParams(input.Ctx) + input.MarketKeeper.SetParams(input.Ctx, params) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, randomPrice) + h := NewHandler(input.MarketKeeper) + + return input, h +} diff --git a/x/mint/keeper.go b/x/mint/keeper.go deleted file mode 100644 index 974acde79..000000000 --- a/x/mint/keeper.go +++ /dev/null @@ -1,155 +0,0 @@ -package mint - -import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// StoreKey is string representation of the store key for mint -const StoreKey = "mint" - -// Keeper is an instance of the Mint keeper module. -// Adds / subtracts balances from accounts and maintains a global state -// of issuance of currencies on the Terra network. -type Keeper struct { - cdc *codec.Codec - key sdk.StoreKey - sk staking.Keeper - bk bank.Keeper - ak auth.AccountKeeper -} - -// NewKeeper creates a new instance of the mint module. -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk staking.Keeper, bk bank.Keeper, ak auth.AccountKeeper) Keeper { - return Keeper{ - cdc: cdc, - key: key, - sk: sk, - bk: bk, - ak: ak, - } -} - -// Mint credits {coin} to the {recipient} account, and reflects the increase in issuance -func (k Keeper) Mint(ctx sdk.Context, recipient sdk.AccAddress, coin sdk.Coin) (err sdk.Error) { - - _, _, err = k.bk.AddCoins(ctx, recipient, sdk.Coins{coin}) - if err != nil { - return err - } - - if coin.Denom == assets.MicroLunaDenom { - pool := k.sk.GetPool(ctx) - pool.NotBondedTokens = pool.NotBondedTokens.Add(coin.Amount) - k.sk.SetPool(ctx, pool) - } - - return k.ChangeIssuance(ctx, coin.Denom, coin.Amount) -} - -// Burn deducts {coin} from the {payer} account, and reflects the decrease in issuance -func (k Keeper) Burn(ctx sdk.Context, payer sdk.AccAddress, coin sdk.Coin) (err sdk.Error) { - _, _, err = k.bk.SubtractCoins(ctx, payer, sdk.Coins{coin}) - if err != nil { - return err - } - - if coin.Denom == assets.MicroLunaDenom { - pool := k.sk.GetPool(ctx) - pool.NotBondedTokens = pool.NotBondedTokens.Sub(coin.Amount) - k.sk.SetPool(ctx, pool) - } - - return k.ChangeIssuance(ctx, coin.Denom, coin.Amount.Neg()) -} - -// ChangeIssuance updates the issuance to reflect -func (k Keeper) ChangeIssuance(ctx sdk.Context, denom string, delta sdk.Int) (err sdk.Error) { - store := ctx.KVStore(k.key) - curDay := sdk.NewInt(ctx.BlockHeight() / util.BlocksPerDay) - - // If genesis issuance is not on disk, GetIssuance will do a fresh read of account balances - // and the change in issuance should be reported automatically. - if !store.Has(keyIssuance(denom, sdk.ZeroInt())) { - k.GetIssuance(ctx, denom, curDay) - return - } - - curIssuance := k.GetIssuance(ctx, denom, curDay) - newIssuance := curIssuance.Add(delta) - - if newIssuance.IsNegative() { - err = sdk.ErrInternal("Issuance should never fall below 0") - } else { - bz := k.cdc.MustMarshalBinaryLengthPrefixed(newIssuance) - store.Set(keyIssuance(denom, curDay), bz) - } - - return -} - -// GetIssuance fetches the total issuance count of the coin matching {denom}. If the {day} applies -// to a previous period, fetches the last stored snapshot issuance of the coin. For virgin calls, -// iterates through the accountkeeper and computes the genesis issuance. -func (k Keeper) GetIssuance(ctx sdk.Context, denom string, day sdk.Int) (issuance sdk.Int) { - store := ctx.KVStore(k.key) - - if bz := store.Get(keyIssuance(denom, day)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &issuance) - } else { - // Genesis epoch; nothing exists in store so we must read it - // from accountkeeper - if day.LTE(sdk.ZeroInt()) { - issuance = sdk.ZeroInt() - countIssuance := func(acc auth.Account) (stop bool) { - issuance = issuance.Add(acc.GetCoins().AmountOf(denom)) - return false - } - k.ak.IterateAccounts(ctx, countIssuance) - } else { - // Fetch the issuance snapshot of the previous epoch - issuance = k.GetIssuance(ctx, denom, day.Sub(sdk.OneInt())) - } - - // Set issuance to the store - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(issuance) - store.Set(keyIssuance(denom, day), bz) - } - - return -} - -// PeekEpochSeigniorage retrieves the size of the seigniorage pool at epoch -func (k Keeper) PeekEpochSeigniorage(ctx sdk.Context, epoch sdk.Int) (epochSeigniorage sdk.Int) { - - daysPerEpoch := util.BlocksPerEpoch / util.BlocksPerDay - epochLastDay := epoch.Add(sdk.OneInt()).MulRaw(daysPerEpoch).Sub(sdk.OneInt()) - - //fmt.Println(epochLastDay) - today := sdk.NewInt(ctx.BlockHeight() / util.BlocksPerDay) - if epochLastDay.GT(today) { - epochLastDay = today - } - - prevEpochLastDay := epochLastDay.SubRaw(daysPerEpoch) - if prevEpochLastDay.IsNegative() { - prevEpochLastDay = sdk.ZeroInt() - } - - prevEpochIssuance := k.GetIssuance(ctx, assets.MicroLunaDenom, prevEpochLastDay) - epochIssuance := k.GetIssuance(ctx, assets.MicroLunaDenom, epochLastDay) - epochSeigniorage = prevEpochIssuance.Sub(epochIssuance) - - if epochSeigniorage.LT(sdk.ZeroInt()) { - epochSeigniorage = sdk.ZeroInt() - } - - return -} diff --git a/x/mint/keeper_keys.go b/x/mint/keeper_keys.go deleted file mode 100644 index e828c8de3..000000000 --- a/x/mint/keeper_keys.go +++ /dev/null @@ -1,21 +0,0 @@ -package mint - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// nolint -var ( - prefixIssuance = []byte("issuance") - prefixSeignioragePool = []byte("seigniorage_pool") -) - -func keyIssuance(denom string, day sdk.Int) []byte { - return []byte(fmt.Sprintf("%s:%s:%s", prefixIssuance, denom, day)) -} - -func keySeignioragePool(epoch sdk.Int) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixSeignioragePool, epoch)) -} diff --git a/x/mint/keeper_test.go b/x/mint/keeper_test.go deleted file mode 100644 index 489763d84..000000000 --- a/x/mint/keeper_test.go +++ /dev/null @@ -1,245 +0,0 @@ -package mint - -import ( - "math" - "math/rand" - "testing" - "time" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/x/staking" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" -) - -var ( - addrs = []sdk.AccAddress{ - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - } - - uSDRAmount = sdk.NewInt(1005).MulRaw(assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - accKeeper auth.AccountKeeper - bankKeeper bank.Keeper - mintKeeper Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - bank.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyMint := sdk.NewKVStoreKey(StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingKeeper.SetParams(ctx, staking.DefaultParams()) - - mintKeeper := NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - for _, addr := range addrs { - _, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, uSDRAmount)}) - require.NoError(t, err) - } - - return testInput{ctx, accKeeper, bankKeeper, mintKeeper} -} - -func TestKeeperIssuance(t *testing.T) { - input := createTestInput(t) - curDay := sdk.ZeroInt() - - // Should be able to claim genesis issunace - issuance := input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, uSDRAmount.MulRaw(3), issuance) - - // Lowering issuance works - err := input.mintKeeper.ChangeIssuance(input.ctx, assets.MicroSDRDenom, sdk.OneInt().MulRaw(assets.MicroUnit).Neg()) - require.Nil(t, err) - issuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, uSDRAmount.MulRaw(3).Sub(sdk.OneInt().MulRaw(assets.MicroUnit)), issuance) - - // ... but not too much - err = input.mintKeeper.ChangeIssuance(input.ctx, assets.MicroSDRDenom, sdk.NewInt(5000).MulRaw(assets.MicroUnit).Neg()) - require.NotNil(t, err) - issuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, uSDRAmount.MulRaw(3).Sub(sdk.OneInt().MulRaw(assets.MicroUnit)), issuance) - - // Raising issuance works, too - err = input.mintKeeper.ChangeIssuance(input.ctx, assets.MicroSDRDenom, sdk.NewInt(986).MulRaw(assets.MicroUnit)) - require.Nil(t, err) - issuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, sdk.NewInt(4000).MulRaw(assets.MicroUnit), issuance) - - // Moving up one epoch inherits the issuance of previous day - curDay = curDay.Add(sdk.OneInt()) - issuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, sdk.NewInt(4000).MulRaw(assets.MicroUnit), issuance) - - // ... Even when you move many days - curDay = curDay.Add(sdk.NewInt(10)) - issuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, sdk.NewInt(4000).MulRaw(assets.MicroUnit), issuance) -} - -func TestKeeperMintBurn(t *testing.T) { - input := createTestInput(t) - curDay := sdk.ZeroInt() - issuance := input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - - // Minting new coins results in an issuance increase - increment := sdk.NewInt(10).MulRaw(assets.MicroUnit) - err := input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroSDRDenom, increment)) - require.Nil(t, err) - newIssuance := input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, issuance.Add(increment), newIssuance) - - // Burning new coins results in an issuance decrease - decrement := sdk.NewInt(10).MulRaw(assets.MicroUnit) - err = input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroSDRDenom, decrement)) - require.Nil(t, err) - newIssuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, issuance, newIssuance) - - // Burning new coins errors if requested to burn too much - decrement = sdk.NewInt(100000).MulRaw(assets.MicroUnit) - err = input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroSDRDenom, decrement)) - require.NotNil(t, err) - newIssuance = input.mintKeeper.GetIssuance(input.ctx, assets.MicroSDRDenom, curDay) - require.Equal(t, issuance, newIssuance) -} - -func TestKeeperSeigniorage(t *testing.T) { - input := createTestInput(t) - - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(100))) - seigniorage := input.mintKeeper.PeekEpochSeigniorage(input.ctx, sdk.NewInt(0)) - require.Equal(t, int64(0), seigniorage.Int64()) - - input.mintKeeper.Burn(input.ctx.WithBlockHeight(util.BlocksPerEpoch-1), addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(100))) - seigniorage = input.mintKeeper.PeekEpochSeigniorage(input.ctx.WithBlockHeight(util.BlocksPerEpoch), sdk.NewInt(0)) - - require.Equal(t, sdk.NewInt(100), seigniorage) -} - -func TestKeeperMintStress(t *testing.T) { - input := createTestInput(t) - rand.Seed(int64(time.Now().Nanosecond())) - - balance := int64(20000) - epochDelta := int64(0) - - // Genesis mint - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(balance))) - - for day := int64(0); day < 100; day++ { - input.ctx = input.ctx.WithBlockHeight(day * util.BlocksPerDay) - amt := rand.Int63()%100 + 1 // Cap at 100; prevents possibility of balance falling negative - option := rand.Int63() % 3 - - switch option { - case 0: // mint - err := input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(amt))) - require.Nil(t, err) - - balance += amt - epochDelta += amt - break - case 1: // burn - err := input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(amt))) - require.Nil(t, err) - - balance -= amt - epochDelta -= amt - break - case 2: // skip - amt = 0 - break - } - - // Ignore first update; just how seigniorage recording works - if day == 0 { - epochDelta = 0 - } - - issuance := input.mintKeeper.GetIssuance(input.ctx, assets.MicroLunaDenom, sdk.NewInt(day)) - require.Equal(t, sdk.NewInt(balance), issuance) - - // last day of epoch - if (day+1)*util.BlocksPerDay%util.BlocksPerEpoch == 0 { - seigniorage := input.mintKeeper.PeekEpochSeigniorage(input.ctx, sdk.NewInt(day)) - require.Equal(t, int64(math.Max(float64(-epochDelta), 0)), seigniorage.Int64()) - epochDelta = 0 - } - } -} diff --git a/x/oracle/abci.go b/x/oracle/abci.go new file mode 100644 index 000000000..f3f68071b --- /dev/null +++ b/x/oracle/abci.go @@ -0,0 +1,87 @@ +package oracle + +import ( + "github.com/terra-project/core/x/oracle/internal/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + + core "github.com/terra-project/core/types" +) + +// EndBlocker is called at the end of every block +func EndBlocker(ctx sdk.Context, k Keeper) { + params := k.GetParams(ctx) + + // Not yet time for a tally + if !core.IsPeriodLastBlock(ctx, params.VotePeriod) { + return + } + + // Reward previous ballot winners + k.RewardPrevBallotWinners(ctx) + + actives := k.GetActiveDenoms(ctx) + votes := k.CollectVotes(ctx) + + // Clear swap rates + for _, activeDenom := range actives { + k.DeletePrice(ctx, activeDenom) + } + + ballotAttendees := make(map[string]bool) + k.StakingKeeper.IterateBondedValidatorsByPower(ctx, func(_ int64, validator exported.ValidatorI) (stop bool) { + key := validator.GetOperator().String() + ballotAttendees[key] = true + return false + }) + + // Iterate through votes and update prices; drop if not enough votes have been achieved. + for denom, ballot := range votes { + if ballotIsPassing(ctx, ballot, k) { + + // Get weighted median prices, and faithful respondants + mod, ballotWinners, ballotLosers := tally(ctx, ballot, k) + + for _, loser := range ballotLosers { + key := loser.String() + if _, exists := ballotAttendees[key]; exists { + ballotAttendees[key] = false // invalid vote + } + } + + // Add claim winners to the store + k.AddClaimPool(ctx, ballotWinners) + + // TODO - update tax-cap + // Set price to the store + k.SetLunaPrice(ctx, denom, mod) + ctx.EventManager().EmitEvent( + sdk.NewEvent(types.EventTypePriceUpdate, + sdk.NewAttribute(types.AttributeKeyDenom, denom), + sdk.NewAttribute(types.AttributeKeyPrice, mod.String()), + ), + ) + } + } + + // Update & check slash condition for the ballot losers + k.HandleBallotAttendees(ctx, ballotAttendees) + + // Clear all prevotes + k.IteratePrevotes(ctx, func(prevote PricePrevote) (stop bool) { + if ctx.BlockHeight() > prevote.SubmitBlock+params.VotePeriod { + k.DeletePrevote(ctx, prevote) + } + + return false + }) + + // Clear all votes + k.IterateVotes(ctx, func(vote PriceVote) (stop bool) { + k.DeleteVote(ctx, vote) + return false + }) + + return +} diff --git a/x/oracle/abci_test.go b/x/oracle/abci_test.go new file mode 100644 index 000000000..c4520b10b --- /dev/null +++ b/x/oracle/abci_test.go @@ -0,0 +1,323 @@ +package oracle + +import ( + "encoding/hex" + "math" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/keeper" + "github.com/terra-project/core/x/oracle/internal/types" +) + +func TestOracleThreshold(t *testing.T) { + input, h := setup(t) + + // Less than the threshold signs, msg fails + // Prevote without price + salt := "1" + bz, err := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res := h(input.Ctx.WithBlockHeight(0), prevoteMsg) + require.True(t, res.IsOK()) + + // Vote and new Prevote + voteMsg := NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx.WithBlockHeight(1), voteMsg) + require.True(t, res.IsOK()) + + EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper) + + _, err = input.OracleKeeper.GetLunaPrice(input.Ctx.WithBlockHeight(1), core.MicroSDRDenom) + require.NotNil(t, err) + + // More than the threshold signs, msg succeeds + salt = "1" + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + salt = "2" + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[1]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + salt = "3" + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[2]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[2]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[2]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper) + + price, err := input.OracleKeeper.GetLunaPrice(input.Ctx.WithBlockHeight(1), core.MicroSDRDenom) + require.Nil(t, err) + require.Equal(t, randomPrice, price) + + val, _ := input.StakingKeeper.GetValidator(input.Ctx, keeper.ValAddrs[2]) + input.StakingKeeper.Delegate(input.Ctx.WithBlockHeight(0), keeper.Addrs[2], stakingAmt.MulRaw(3), sdk.Unbonded, val, false) + + salt = "1" + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + salt = "2" + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[1]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper) + + price, err = input.OracleKeeper.GetLunaPrice(input.Ctx.WithBlockHeight(1), core.MicroSDRDenom) + require.NotNil(t, err) +} + +func TestOracleMultiVote(t *testing.T) { + input, h := setup(t) + + // Less than the threshold signs, msg fails + salt := "1" + bz, err := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res := h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[1]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + res = h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[2]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[2]) + res = h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + bz, err = VoteHash(salt, anotherRandomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + bz, err = VoteHash(salt, anotherRandomPrice, core.MicroSDRDenom, keeper.ValAddrs[1]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + res = h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + bz, err = VoteHash(salt, anotherRandomPrice, core.MicroSDRDenom, keeper.ValAddrs[2]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[2]) + res = h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + // Reveal Price + input.Ctx = input.Ctx.WithBlockHeight(1) + voteMsg := NewMsgPriceVote(anotherRandomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx, voteMsg) + require.True(t, res.IsOK()) + + voteMsg = NewMsgPriceVote(anotherRandomPrice, salt, core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + res = h(input.Ctx, voteMsg) + require.True(t, res.IsOK()) + + voteMsg = NewMsgPriceVote(anotherRandomPrice, salt, core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[2]) + res = h(input.Ctx, voteMsg) + require.True(t, res.IsOK()) + + EndBlocker(input.Ctx, input.OracleKeeper) + + price, err := input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroSDRDenom) + require.Nil(t, err) + require.Equal(t, price, anotherRandomPrice) +} + +func TestOracleDrop(t *testing.T) { + input, h := setup(t) + + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, randomPrice) + + salt := "1" + bz, err := VoteHash(salt, randomPrice, core.MicroKRWDenom, keeper.ValAddrs[0]) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroKRWDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx, prevoteMsg) + + input.Ctx = input.Ctx.WithBlockHeight(1) + voteMsg := NewMsgPriceVote(randomPrice, salt, core.MicroKRWDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx, voteMsg) + + // Immediately swap halt after an illiquid oracle vote + EndBlocker(input.Ctx, input.OracleKeeper) + + _, err = input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroKRWDenom) + require.NotNil(t, err) +} + +func TestOracleTally(t *testing.T) { + input, _ := setup(t) + + ballot := PriceBallot{} + prices, valAddrs, stakingKeeper := types.GenerateRandomTestCase() + input.OracleKeeper.StakingKeeper = stakingKeeper + h := NewHandler(input.OracleKeeper) + for i, price := range prices { + + decPrice := sdk.NewDecWithPrec(int64(price*math.Pow10(keeper.OracleDecPrecision)), int64(keeper.OracleDecPrecision)) + + salt := string(i) + bz, err := VoteHash(salt, decPrice, core.MicroSDRDenom, valAddrs[i]) + require.Nil(t, err) + + prevoteMsg := NewMsgPricePrevote( + hex.EncodeToString(bz), + core.MicroSDRDenom, + sdk.AccAddress(valAddrs[i]), + valAddrs[i], + ) + + res := h(input.Ctx.WithBlockHeight(0), prevoteMsg) + require.True(t, res.IsOK()) + + voteMsg := NewMsgPriceVote( + decPrice, + salt, + core.MicroSDRDenom, + sdk.AccAddress(valAddrs[i]), + valAddrs[i], + ) + + res = h(input.Ctx.WithBlockHeight(1), voteMsg) + require.True(t, res.IsOK()) + + vote := NewPriceVote(decPrice, core.MicroSDRDenom, valAddrs[i]) + ballot = append(ballot, vote) + + // change power of every three validator + if i%3 == 0 { + stakingKeeper.Validators()[i].SetPower(int64(i + 1)) + } + } + + rewardees := []sdk.AccAddress{} + weightedMedian := ballot.WeightedMedian(input.Ctx, stakingKeeper) + standardDeviation := ballot.StandardDeviation(input.Ctx, stakingKeeper) + maxSpread := input.OracleKeeper.RewardBand(input.Ctx).QuoInt64(2) + + if standardDeviation.GT(maxSpread) { + maxSpread = standardDeviation + } + + for _, vote := range ballot { + if vote.Price.GTE(weightedMedian.Sub(maxSpread)) && vote.Price.LTE(weightedMedian.Add(maxSpread)) { + rewardees = append(rewardees, sdk.AccAddress(vote.Voter)) + } + } + + tallyMedian, ballotWinner, _ := tally(input.Ctx, ballot, input.OracleKeeper) + + require.Equal(t, len(rewardees), len(ballotWinner)) + require.Equal(t, tallyMedian.MulInt64(100).TruncateInt(), weightedMedian.MulInt64(100).TruncateInt()) +} + +func TestOracleTallyTiming(t *testing.T) { + input, h := setup(t) + + // all the keeper.Addrs vote for the block ... not last period block yet, so tally fails + for _, addr := range keeper.Addrs { + salt := "1" + bz, err := VoteHash(salt, sdk.OneDec(), core.MicroSDRDenom, sdk.ValAddress(addr)) + require.Nil(t, err) + + prevoteMsg := NewMsgPricePrevote( + hex.EncodeToString(bz), + core.MicroSDRDenom, + addr, + sdk.ValAddress(addr), + ) + + res := h(input.Ctx, prevoteMsg) + require.True(t, res.IsOK()) + + voteMsg := NewMsgPriceVote( + sdk.OneDec(), + salt, + core.MicroSDRDenom, + addr, + sdk.ValAddress(addr), + ) + + res = h(input.Ctx.WithBlockHeight(1), voteMsg) + require.True(t, res.IsOK()) + } + + params := input.OracleKeeper.GetParams(input.Ctx) + params.VotePeriod = 10 // set vote period to 10 for now, for convinience + input.OracleKeeper.SetParams(input.Ctx, params) + require.Equal(t, 0, int(input.Ctx.BlockHeight())) + + EndBlocker(input.Ctx, input.OracleKeeper) + require.Equal(t, 0, countClaimPool(input.Ctx, input.OracleKeeper)) + + input.Ctx = input.Ctx.WithBlockHeight(params.VotePeriod - 1) + + EndBlocker(input.Ctx, input.OracleKeeper) + require.Equal(t, len(keeper.Addrs), countClaimPool(input.Ctx, input.OracleKeeper)) +} + +func countClaimPool(ctx sdk.Context, OracleKeeper Keeper) (claimCount int) { + OracleKeeper.IterateClaimPool(ctx, func(recipient sdk.ValAddress, weight int64) (stop bool) { + claimCount++ + return false + }) + + return claimCount +} + +func TestOracleRewardDistribution(t *testing.T) { + input, h := setup(t) + + salt := "1" + bz, _ := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg := NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + salt = "2" + bz, _ = VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[1]) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(0), prevoteMsg) + + voteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[1]) + h(input.Ctx.WithBlockHeight(1), voteMsg) + + moduleAcc := input.SupplyKeeper.GetModuleAccount(input.Ctx.WithBlockHeight(1), ModuleName) + err := moduleAcc.SetCoins(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, stakingAmt.MulRaw(100)))) + require.NoError(t, err) + + input.SupplyKeeper.SetModuleAccount(input.Ctx.WithBlockHeight(1), moduleAcc) + + EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper) + EndBlocker(input.Ctx.WithBlockHeight(2), input.OracleKeeper) + + expectedRewardAmt := input.OracleKeeper.RewardFraction(input.Ctx).MulInt(stakingAmt.MulRaw(50)).TruncateInt() + rewards := input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[0]) + require.Equal(t, expectedRewardAmt, rewards.AmountOf(core.MicroSDRDenom).TruncateInt()) + rewards = input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[1]) + require.Equal(t, expectedRewardAmt, rewards.AmountOf(core.MicroSDRDenom).TruncateInt()) +} diff --git a/x/oracle/alias.go b/x/oracle/alias.go new file mode 100644 index 000000000..7e4ae679a --- /dev/null +++ b/x/oracle/alias.go @@ -0,0 +1,140 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/oracle/internal/types/ +// ALIASGEN: github.com/terra-project/core/x/oracle/internal/keeper/ +package oracle + +import ( + "github.com/terra-project/core/x/oracle/internal/keeper" + "github.com/terra-project/core/x/oracle/internal/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + CodeUnknownDenom = types.CodeUnknownDenom + CodeInvalidPrice = types.CodeInvalidPrice + CodeVoterNotValidator = types.CodeVoterNotValidator + CodeInvalidVote = types.CodeInvalidVote + CodeNoVotingPermission = types.CodeNoVotingPermission + CodeInvalidHashLength = types.CodeInvalidHashLength + CodeInvalidPrevote = types.CodeInvalidPrevote + CodeVerificationFailed = types.CodeVerificationFailed + CodeNotRevealPeriod = types.CodeNotRevealPeriod + CodeInvalidSaltLength = types.CodeInvalidSaltLength + CodeInvalidMsgFormat = types.CodeInvalidMsgFormat + CodeMissingVotingInfo = types.CodeMissingVotingInfo + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + DefaultParamspace = types.DefaultParamspace + DefaultVotePeriod = types.DefaultVotePeriod + DefaultVotesWindow = types.DefaultVotesWindow + QueryParameters = types.QueryParameters + QueryPrice = types.QueryPrice + QueryActives = types.QueryActives + QueryPrevotes = types.QueryPrevotes + QueryVotes = types.QueryVotes + QueryFeederDelegation = types.QueryFeederDelegation + QueryVotingInfo = types.QueryVotingInfo + QueryVotingInfos = types.QueryVotingInfos +) + +var ( + // functions aliases + NewClaim = types.NewClaim + RegisterCodec = types.RegisterCodec + ErrInvalidHashLength = types.ErrInvalidHashLength + ErrUnknownDenomination = types.ErrUnknownDenomination + ErrInvalidPrice = types.ErrInvalidPrice + ErrVoterNotValidator = types.ErrVoterNotValidator + ErrVerificationFailed = types.ErrVerificationFailed + ErrNoPrevote = types.ErrNoPrevote + ErrNoVote = types.ErrNoVote + ErrNoVotingPermission = types.ErrNoVotingPermission + ErrNotRevealPeriod = types.ErrNotRevealPeriod + ErrInvalidSaltLength = types.ErrInvalidSaltLength + ErrInvalidMsgFormat = types.ErrInvalidMsgFormat + ErrNoVotingInfoFound = types.ErrNoVotingInfoFound + NewGenesisState = types.NewGenesisState + NewMissedVote = types.NewMissedVote + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis + GetPrevoteKey = types.GetPrevoteKey + GetVoteKey = types.GetVoteKey + GetPriceKey = types.GetPriceKey + GetFeederDelegationKey = types.GetFeederDelegationKey + GetClaimKey = types.GetClaimKey + GetMissedVoteBitArrayPrefixKey = types.GetMissedVoteBitArrayPrefixKey + GetMissedVoteBitArrayKey = types.GetMissedVoteBitArrayKey + GetVotingInfoKey = types.GetVotingInfoKey + NewMsgPricePrevote = types.NewMsgPricePrevote + NewMsgPriceVote = types.NewMsgPriceVote + NewMsgDelegateFeederPermission = types.NewMsgDelegateFeederPermission + DefaultParams = types.DefaultParams + NewQueryPriceParams = types.NewQueryPriceParams + NewQueryPrevotesParams = types.NewQueryPrevotesParams + NewQueryVotesParams = types.NewQueryVotesParams + NewQueryFeederDelegationParams = types.NewQueryFeederDelegationParams + NewQueryVotingInfoParams = types.NewQueryVotingInfoParams + NewQueryVotingInfosParams = types.NewQueryVotingInfosParams + NewPricePrevote = types.NewPricePrevote + VoteHash = types.VoteHash + NewPriceVote = types.NewPriceVote + NewVotingInfo = types.NewVotingInfo + NewKeeper = keeper.NewKeeper + ParamKeyTable = keeper.ParamKeyTable + NewQuerier = keeper.NewQuerier + + // variable aliases + ModuleCdc = types.ModuleCdc + PrevoteKey = types.PrevoteKey + VoteKey = types.VoteKey + PriceKey = types.PriceKey + FeederDelegationKey = types.FeederDelegationKey + ClaimKey = types.ClaimKey + MissedVoteBitArrayKey = types.MissedVoteBitArrayKey + VotingInfoKey = types.VotingInfoKey + ParamStoreKeyVotePeriod = types.ParamStoreKeyVotePeriod + ParamStoreKeyVoteThreshold = types.ParamStoreKeyVoteThreshold + ParamStoreKeyRewardBand = types.ParamStoreKeyRewardBand + ParamStoreKeyRewardFraction = types.ParamStoreKeyRewardFraction + ParamStoreKeyVotesWindow = types.ParamStoreKeyVotesWindow + ParamStoreKeyMinValidVotesPerWindow = types.ParamStoreKeyMinValidVotesPerWindow + ParamStoreKeySlashFraction = types.ParamStoreKeySlashFraction + DefaultVoteThreshold = types.DefaultVoteThreshold + DefaultRewardBand = types.DefaultRewardBand + DefaultRewardFraction = types.DefaultRewardFraction + DefaultMinValidVotesPerWindow = types.DefaultMinValidVotesPerWindow + DefaultSlashFraction = types.DefaultSlashFraction +) + +type ( + PriceBallot = types.PriceBallot + Claim = types.Claim + ClaimPool = types.ClaimPool + DenomList = types.DenomList + StakingKeeper = types.StakingKeeper + DistributionKeeper = types.DistributionKeeper + SupplyKeeper = types.SupplyKeeper + GenesisState = types.GenesisState + MissedVote = types.MissedVote + MsgPricePrevote = types.MsgPricePrevote + MsgPriceVote = types.MsgPriceVote + MsgDelegateFeederPermission = types.MsgDelegateFeederPermission + Params = types.Params + QueryPriceParams = types.QueryPriceParams + QueryPrevotesParams = types.QueryPrevotesParams + QueryVotesParams = types.QueryVotesParams + QueryFeederDelegationParams = types.QueryFeederDelegationParams + QueryVotingInfoParams = types.QueryVotingInfoParams + QueryVotingInfosParams = types.QueryVotingInfosParams + PricePrevote = types.PricePrevote + PricePrevotes = types.PricePrevotes + PriceVote = types.PriceVote + PriceVotes = types.PriceVotes + VotingInfo = types.VotingInfo + Hooks = keeper.Hooks + Keeper = keeper.Keeper +) diff --git a/x/oracle/ballot_test.go b/x/oracle/ballot_test.go deleted file mode 100644 index 6dae5900e..000000000 --- a/x/oracle/ballot_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package oracle - -import ( - "math" - "math/rand" - "testing" - "time" - - "github.com/terra-project/core/types/assets" - - "github.com/tendermint/tendermint/crypto/secp256k1" - mcVal "github.com/terra-project/core/types/mock" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" -) - -func generateRandomTestCase() (prices []float64, valAccAddrs []sdk.AccAddress, mockValset mcVal.MockValset) { - mockValset = mcVal.NewMockValSet() - valAccAddrs = []sdk.AccAddress{} - base := math.Pow10(oracleDecPrecision) - - rand.Seed(int64(time.Now().Nanosecond())) - numInputs := 10 + (rand.Int() % 100) - for i := 0; i < numInputs; i++ { - price := float64(int64(rand.Float64()*base)) / base - prices = append(prices, price) - - valAccAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - valAccAddrs = append(valAccAddrs, valAccAddr) - - power := sdk.NewInt(rand.Int63() % 1000) - mockValAddr := sdk.ValAddress(valAccAddr.Bytes()) - mockVal := mcVal.NewMockValidator(mockValAddr, power) - - mockValset.Validators = append(mockValset.Validators, mockVal) - } - - return -} - -func checkFloatEquality(a sdk.Dec, b float64, precision int) bool { - base := math.Pow10(precision) - - a2 := a.MulInt64(int64(base)).TruncateInt64() - b2 := int64(b * base) - - return a2 == b2 -} - -func TestPBPower(t *testing.T) { - input := createTestInput(t) - - _, valAccAddrs, mockValset := generateRandomTestCase() - pb := PriceBallot{} - ballotPower := sdk.ZeroInt() - - for i := 0; i < len(mockValset.Validators); i++ { - vote := NewPriceVote(sdk.ZeroDec(), assets.MicroSDRDenom, sdk.ValAddress(valAccAddrs[i])) - pb = append(pb, vote) - - valPower, err := vote.getPower(input.ctx, mockValset) - require.Nil(t, err) - - ballotPower = ballotPower.Add(valPower) - } - - require.Equal(t, ballotPower, pb.power(input.ctx, mockValset)) - - // Mix in a fake validator, the total power should not have changed. - fakeVote := NewPriceVote(sdk.OneDec(), assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - pb = append(pb, fakeVote) - require.Equal(t, ballotPower, pb.power(input.ctx, mockValset)) -} - -func TestPBWeightedMedian(t *testing.T) { - input := createTestInput(t) - tests := []struct { - inputs []float64 - weights []int64 - isValidator []bool - median sdk.Dec - }{ - { - // Supermajority one number - []float64{1.0, 2.0, 10.0, 100000.0}, - []int64{1, 1, 100, 1}, - []bool{true, true, true, true}, - sdk.NewDecWithPrec(10, 0), - }, - { - // Adding fake validator doesn't change outcome - []float64{1.0, 2.0, 10.0, 100000.0, 10000000000}, - []int64{1, 1, 100, 1, 10000}, - []bool{true, true, true, true, false}, - sdk.NewDecWithPrec(10, 0), - }, - { - // Tie votes - []float64{1.0, 2.0, 3.0, 4.0}, - []int64{1, 100, 100, 1}, - []bool{true, true, true, true}, - sdk.NewDecWithPrec(2, 0), - }, - { - // No votes - []float64{}, - []int64{}, - []bool{true, true, true, true}, - sdk.NewDecWithPrec(0, 0), - }, - } - - mockValset := mcVal.NewMockValSet() - base := math.Pow10(oracleDecPrecision) - for _, tc := range tests { - pb := PriceBallot{} - for i, input := range tc.inputs { - valAccAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - - power := sdk.NewInt(tc.weights[i]) - mockValAddr := sdk.ValAddress(valAccAddr.Bytes()) - mockVal := mcVal.NewMockValidator(mockValAddr, power) - - if tc.isValidator[i] { - mockValset.Validators = append(mockValset.Validators, mockVal) - } - vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*base), int64(oracleDecPrecision)), assets.MicroSDRDenom, sdk.ValAddress(valAccAddr)) - pb = append(pb, vote) - } - - require.Equal(t, tc.median, pb.weightedMedian(input.ctx, mockValset)) - } -} - -// func TestPBTally(t *testing.T) { -// _, addrs, _, _ := mock.CreateGenAccounts(4, sdk.Coins{}) -// tests := []struct { -// inputs []float64 -// weights []int64 -// rewardees []sdk.AccAddress -// }{ -// { -// // Supermajority one number -// []float64{1.0, 2.0, 10.0, 100000.0}, -// []int64{1, 1, 100, 1}, -// []sdk.AccAddress{addrs[2]}, -// }, -// { -// // Tie votes -// []float64{1.0, 2.0, 3.0, 4.0}, -// []int64{1, 100, 100, 1}, -// []sdk.AccAddress{addrs[1]}, -// }, -// { -// // No votes -// []float64{}, -// []int64{}, -// []sdk.AccAddress{}, -// }, - -// { -// // Lots of random votes -// []float64{1.0, 78.48, 78.11, 79.0}, -// []int64{1, 51, 79, 33}, -// []sdk.AccAddress{addrs[1], addrs[2], addrs[3]}, -// }, -// } - -// for _, tc := range tests { -// pb := PriceBallot{} -// for i, input := range tc.inputs { -// vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*100), 2), "", -// sdk.NewInt(tc.weights[i]), addrs[i]) -// pb = append(pb, vote) -// } - -// _, rewardees := pb.tally() -// require.Equal(t, len(tc.rewardees), len(rewardees)) -// } -// } diff --git a/x/oracle/client/cli/cli_test.go b/x/oracle/client/cli/cli_test.go deleted file mode 100644 index 2a5190aba..000000000 --- a/x/oracle/client/cli/cli_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/spf13/cobra" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/terra-project/core/x/oracle" -) - -func TestPricePrevoteTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - oracleTxCmd := &cobra.Command{ - Use: "oracle", - Short: "Oracle transaction subcommands", - } - - txCmd.AddCommand(oracleTxCmd) - - oracleTxCmd.AddCommand(client.PostCommands( - GetCmdPricePrevote(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `oracle`, - `prevote`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--denom=ukrw`, - `--price=5555.55`, - `--salt=1234`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestPriceVoteTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - oracleTxCmd := &cobra.Command{ - Use: "oracle", - Short: "Oracle transaction subcommands", - } - - txCmd.AddCommand(oracleTxCmd) - - oracleTxCmd.AddCommand(client.PostCommands( - GetCmdPriceVote(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `oracle`, - `vote`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--denom=ukrw`, - `--price=5555.55`, - `--salt=1234`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestDelegateFeederPermissionTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - oracleTxCmd := &cobra.Command{ - Use: "oracle", - Short: "Oracle transaction subcommands", - } - - txCmd.AddCommand(oracleTxCmd) - - oracleTxCmd.AddCommand(client.PostCommands( - GetCmdDelegateFeederPermission(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `oracle`, - `set-feeder`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--feeder=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestGetCmdQueryPrice(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryPriceCmd := GetCmdQueryPrice(oracle.QuerierRoute, cdc) - - // Name check - require.Equal(t, oracle.QueryPrice, queryPriceCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryPriceCmd.Args)) - - // Check Flags - denomFlag := queryPriceCmd.Flag(flagDenom) - require.NotNil(t, denomFlag) - require.Equal(t, []string{"true"}, denomFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestGetCmdQueryActive(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryActiveCmd := GetCmdQueryActive(oracle.QuerierRoute, cdc) - - // Name check - require.Equal(t, oracle.QueryActive, queryActiveCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryActiveCmd.Args)) -} - -func TestGetCmdQueryVotes(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryVotesCmd := GetCmdQueryVotes(oracle.QuerierRoute, cdc) - - // Name check - require.Equal(t, oracle.QueryVotes, queryVotesCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryVotesCmd.Args)) - - // Check Flags - denomFlag := queryVotesCmd.Flag(flagDenom) - require.NotNil(t, denomFlag) - require.Equal(t, []string{"true"}, denomFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - voterFlag := queryVotesCmd.Flag(flagValidator) - require.NotNil(t, voterFlag) -} - -func TestGetCmdQueryPrevotes(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryPrevotesCmd := GetCmdQueryPrevotes(oracle.QuerierRoute, cdc) - - // Name check - require.Equal(t, oracle.QueryPrevotes, queryPrevotesCmd.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryPrevotesCmd.Args)) - - // Check Flags - denomFlag := queryPrevotesCmd.Flag(flagDenom) - require.NotNil(t, denomFlag) - require.Equal(t, []string{"true"}, denomFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - voterFlag := queryPrevotesCmd.Flag(flagValidator) - require.NotNil(t, voterFlag) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParamsCmd := GetCmdQueryParams(oracle.QuerierRoute, cdc) - - // Name check - require.Equal(t, queryParamsCmd.Name(), oracle.QueryParams) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParamsCmd.Args)) -} diff --git a/x/oracle/client/cli/query.go b/x/oracle/client/cli/query.go index dec0bd2fc..2647d54e0 100644 --- a/x/oracle/client/cli/query.go +++ b/x/oracle/client/cli/query.go @@ -4,20 +4,43 @@ import ( "fmt" "strings" - "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/oracle/internal/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" - "github.com/spf13/viper" ) +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(cdc *codec.Codec) *cobra.Command { + oracleQueryCmd := &cobra.Command{ + Use: "oracle", + Short: "Querying commands for the oracle module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + oracleQueryCmd.AddCommand(client.GetCommands( + GetCmdQueryPrice(cdc), + GetCmdQueryVotes(cdc), + GetCmdQueryPrevotes(cdc), + GetCmdQueryActive(cdc), + GetCmdQueryParams(cdc), + GetCmdQueryFeederDelegation(cdc), + GetCmdQueryVotingInfo(cdc), + )...) + + return oracleQueryCmd + +} + // GetCmdQueryPrice implements the query price command. -func GetCmdQueryPrice(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryPrice(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryPrice, - Args: cobra.NoArgs, + Use: "price [denom]", + Args: cobra.ExactArgs(1), Short: "Query the current Luna exchange rate w.r.t an asset", Long: strings.TrimSpace(` Query the current exchange rate of Luna with an asset. You can find the current list of active denoms by running: terracli query oracle active @@ -27,48 +50,47 @@ $ terracli query oracle price --denom ukrw RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - denom := viper.GetString(flagDenom) - if denom == "" { - return fmt.Errorf("--denom flag is required") + denom := args[0] + + params := types.NewQueryPriceParams(denom) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + return err } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", queryRoute, oracle.QueryPrice, denom), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPrice), bz) if err != nil { return err } - var price oracle.QueryPriceResponse + var price sdk.Dec cdc.MustUnmarshalJSON(res, &price) return cliCtx.PrintOutput(price) }, } - - cmd.Flags().String(flagDenom, "", "target denom to get the price") - - cmd.MarkFlagRequired(flagDenom) return cmd } // GetCmdQueryActive implements the query active command. -func GetCmdQueryActive(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryActive(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryActive, + Use: "actives", Args: cobra.NoArgs, Short: "Query the active list of Terra assets recognized by the oracle", Long: strings.TrimSpace(` -Query the active list of Terra assets recognized by the oracle. +Query the active list of Terra assets recognized by the types. -$ terracli query oracle active +$ terracli query oracle actives `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryActive), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryActives), nil) if err != nil { return err } - var actives oracle.QueryActiveResponse + var actives types.DenomList cdc.MustUnmarshalJSON(res, &actives) return cliCtx.PrintOutput(actives) }, @@ -78,134 +100,124 @@ $ terracli query oracle active } // GetCmdQueryVotes implements the query vote command. -func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryVotes(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryVotes, - Args: cobra.NoArgs, + Use: "votes [denom] [validator]", + Args: cobra.RangeArgs(1, 2), Short: "Query outstanding oracle votes, filtered by denom and voter address.", Long: strings.TrimSpace(` Query outstanding oracle votes, filtered by denom and voter address. -$ terracli query oracle votes --denom="uusd" --validator="terravaloper..." +$ terracli query oracle votes uusd terravaloper... +$ terracli query oracle votes uusd returns oracle votes submitted by the validator for the denom uusd `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - denom := viper.GetString(flagDenom) + denom := args[0] // Check voter address exists, then valids var voterAddress sdk.ValAddress + if len(args) >= 2 { + bechVoterAddr := args[1] - bechVoterAddr := viper.GetString(flagValidator) - if len(bechVoterAddr) != 0 { var err error - voterAddress, err = sdk.ValAddressFromBech32(bechVoterAddr) if err != nil { return err } } - params := oracle.NewQueryVotesParams(voterAddress, denom) + params := types.NewQueryVotesParams(voterAddress, denom) bz, err := cdc.MarshalJSON(params) if err != nil { return err } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryVotes), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryVotes), bz) if err != nil { return err } - var matchingVotes oracle.QueryVotesResponse + var matchingVotes types.PriceVotes cdc.MustUnmarshalJSON(res, &matchingVotes) return cliCtx.PrintOutput(matchingVotes) }, } - cmd.Flags().String(flagDenom, "", "filter by votes matching the denom") - cmd.Flags().String(flagValidator, "", "(optional) filter by votes by validator") - - cmd.MarkFlagRequired(flagDenom) - return cmd } // GetCmdQueryPrevotes implements the query prevote command. -func GetCmdQueryPrevotes(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryPrevotes(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryPrevotes, - Args: cobra.NoArgs, + Use: "prevotes [denom] [validator]", + Args: cobra.RangeArgs(1, 2), Short: "Query outstanding oracle prevotes, filtered by denom and voter address.", Long: strings.TrimSpace(` Query outstanding oracle prevotes, filtered by denom and voter address. -$ terracli query oracle prevotes --denom="uusd" --validator="terravaloper..." +$ terracli query oracle prevotes uusd terravaloper... +$ terracli query oracle prevotes uusd returns oracle prevotes submitted by the validator for denom uusd `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - denom := viper.GetString(flagDenom) + denom := args[0] // Check voter address exists, then valids var voterAddress sdk.ValAddress + if len(args) >= 2 { + bechVoterAddr := args[1] - bechVoterAddr := viper.GetString(flagValidator) - if len(bechVoterAddr) != 0 { var err error - voterAddress, err = sdk.ValAddressFromBech32(bechVoterAddr) if err != nil { return err } } - params := oracle.NewQueryPrevotesParams(voterAddress, denom) + params := types.NewQueryPrevotesParams(voterAddress, denom) bz, err := cdc.MarshalJSON(params) if err != nil { return err } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryPrevotes), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPrevotes), bz) if err != nil { return err } - var matchingPrevotes oracle.QueryPrevotesResponse + var matchingPrevotes types.PricePrevotes cdc.MustUnmarshalJSON(res, &matchingPrevotes) return cliCtx.PrintOutput(matchingPrevotes) }, } - cmd.Flags().String(flagDenom, "", "filter by prevotes matching the denom") - cmd.Flags().String(flagValidator, "", "(optional) filter by prevotes by validator") - - cmd.MarkFlagRequired(flagDenom) - return cmd } // GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryParams, + Use: "params", Args: cobra.NoArgs, Short: "Query the current Oracle params", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryParams), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) if err != nil { return err } - var params oracle.Params + var params types.Params cdc.MustUnmarshalJSON(res, ¶ms) return cliCtx.PrintOutput(params) }, @@ -215,47 +227,76 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { } // GetCmdQueryFeederDelegation implements the query feeder delegation command -func GetCmdQueryFeederDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryFeederDelegation(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: oracle.QueryFeederDelegation, + Use: "feeder-delegation [validator]", + Args: cobra.ExactArgs(1), Short: "Query the oracle feeder delegate account", Long: strings.TrimSpace(` Query the account the validator's oracle voting right is delegated to. -$ terracli query oracle feeder --validator terravaloper... +$ terracli query oracle feeder terravaloper... `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - valString := viper.GetString(flagValidator) - if len(valString) == 0 { - return fmt.Errorf("--validator flag is required") - } + valString := args[0] validator, err := sdk.ValAddressFromBech32(valString) if err != nil { return err } - params := oracle.NewQueryFeederDelegationParams(validator) + params := types.NewQueryFeederDelegationParams(validator) bz, err := cdc.MarshalJSON(params) if err != nil { return err } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryFeederDelegation), bz) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFeederDelegation), bz) if err != nil { return err } - var delegatee oracle.QueryFeederDelegationResponse + var delegatee sdk.AccAddress cdc.MustUnmarshalJSON(res, &delegatee) return cliCtx.PrintOutput(delegatee) }, } - cmd.Flags().String(flagValidator, "", "validator which owns the oracle voting rights") + return cmd +} - cmd.MarkFlagRequired(flagValidator) +// GetCmdQueryVotingInfo implements the command to query voting info. +func GetCmdQueryVotingInfo(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "voting-info [validator-addr]", + Short: "Query a validator's voting information", + Long: strings.TrimSpace(`Use a validators' address to find the voting-info for that validator: - return cmd +$ query oracle voting-info terravaloper... +`), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + key := types.GetVotingInfoKey(valAddr) + res, _, err := cliCtx.QueryStore(key, types.QuerierRoute) + if err != nil { + return err + } + + if len(res) == 0 { + return fmt.Errorf("Validator %s not found in oracle store", valAddr) + } + + var votingInfo types.VotingInfo + cdc.MustUnmarshalBinaryLengthPrefixed(res, &votingInfo) + return cliCtx.PrintOutput(votingInfo) + }, + } } diff --git a/x/oracle/client/cli/tx.go b/x/oracle/client/cli/tx.go index 08e0ca688..09791c4a2 100644 --- a/x/oracle/client/cli/tx.go +++ b/x/oracle/client/cli/tx.go @@ -7,33 +7,42 @@ import ( "github.com/pkg/errors" - "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/oracle/internal/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/spf13/cobra" - "github.com/spf13/viper" ) -const ( - flagSalt = "salt" - flagPrice = "price" - flagHash = "Hash" +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + oracleTxCmd := &cobra.Command{ + Use: "oracle", + Short: "Oracle transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } - flagDenom = "denom" - flagValidator = "validator" - flagFeeder = "feeder" + oracleTxCmd.AddCommand(client.PostCommands( + GetCmdPricePrevote(cdc), + GetCmdPriceVote(cdc), + GetCmdDelegateFeederPermission(cdc), + )...) - flagOffline = "offline" -) + return oracleTxCmd +} // GetCmdPricePrevote will create a pricePrevote tx and sign it with the given key. func GetCmdPricePrevote(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "prevote", + Use: "prevote [salt] [price] [validator]", + Args: cobra.RangeArgs(2, 3), Short: "Submit an oracle prevote for the price of Luna", Long: strings.TrimSpace(` Submit an oracle prevote for the price of Luna denominated in the input denom. @@ -41,203 +50,139 @@ The purpose of prevote is to hide vote price with hash which is formatted as hex string in SHA256("salt:price:denom:voter") # Prevote -$ terracli tx oracle prevote --denom "ukrw" --hash "72f374291b0428453bf481ec9d4b0b2440299b62" --from mykey -$ terracli tx oracle prevote --denom "ukrw" --price "8888" --salt "4321" --from mykey +$ terracli tx oracle prevote 1234 8888.0ukrw -where "ukrw" is the denominating currency, and "8890" is the price of micro Luna in micro KRW from the voter's point of view. +where "ukrw" is the denominating currency, and "8888.0" is the price of micro Luna in micro KRW from the voter's point of view. If voting from a voting delegate, set "validator" to the address of the validator to vote on behalf of: -$ terracli tx oracle prevote --denom "ukrw" --price "8890" --from mykey --validator terravaloper1... +$ terracli tx oracle prevote 1234 8888.0ukrw terravaloper1... `), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - offline := viper.GetBool(flagOffline) + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } + salt := args[0] + price, err := sdk.ParseDecCoin(args[1]) + if err != nil { + return fmt.Errorf("given price {%s} is not a valid format; price should be formatted as DecCoin", price) } // Get from address voter := cliCtx.GetFromAddress() - denom := viper.GetString(flagDenom) - priceStr := viper.GetString(flagPrice) - hash := viper.GetString(flagHash) - salt := viper.GetString(flagSalt) - - if len(hash) == 0 && !(len(priceStr) > 0 && len(salt) > 0) { - return fmt.Errorf("hash or (price, salt) should be given") - } + denom := price.Denom + amount := price.Amount // By default the voter is voting on behalf of itself validator := sdk.ValAddress(voter) - // Override validator if flag is set - valStr := viper.GetString(flagValidator) - if len(valStr) != 0 { - parsedVal, err := sdk.ValAddressFromBech32(valStr) + // Override validator if validator is given + if len(args) == 3 { + parsedVal, err := sdk.ValAddressFromBech32(args[2]) if err != nil { return errors.Wrap(err, "validator address is invalid") } validator = parsedVal } - if len(hash) == 0 { - price, err := sdk.NewDecFromStr(priceStr) - if err != nil { - return fmt.Errorf("given price {%s} is not a valid format; price should be formatted as float", priceStr) - } - - hashBytes, err2 := oracle.VoteHash(salt, price, denom, validator) - if err2 != nil { - return err2 - } - - hash = hex.EncodeToString(hashBytes) + hashBytes, err := types.VoteHash(salt, amount, denom, validator) + if err != nil { + return err } - msg := oracle.NewMsgPricePrevote(hash, denom, voter, validator) - err := msg.ValidateBasic() + hash := hex.EncodeToString(hashBytes) + + msg := types.NewMsgPricePrevote(hash, denom, voter, validator) + err = msg.ValidateBasic() if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - cmd.Flags().String(flagDenom, "", "denominating currency") - cmd.Flags().String(flagValidator, "", "validator on behalf of which to vote (for delegated feeders)") - cmd.Flags().String(flagHash, "", "hex string; hash of next vote; empty == skip prevote") - cmd.Flags().String(flagPrice, "", "price of Luna in denom currency is to make provte hash; this field is required to submit prevote in case absence of hash") - cmd.Flags().String(flagSalt, "", "salt is to make prevote hash; this field is required to submit prevote in case absence of hash") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection the node can still build and sign tx") - - cmd.MarkFlagRequired(flagDenom) - return cmd } // GetCmdPriceVote will create a priceVote tx and sign it with the given key. func GetCmdPriceVote(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "vote", + Use: "vote [salt] [price] [validator]", + Args: cobra.RangeArgs(2, 3), Short: "Submit an oracle vote for the price of Luna", Long: strings.TrimSpace(` Submit a vote for the price of Luna denominated in the input denom. Companion to a prevote submitted in the previous vote period. -$ terracli tx oracle vote --denom "ukrw" --price "8890" --salt "1234" --from mykey +$ terracli tx oracle vote 1234 8890.0ukrw -where "ukrw" is the denominating currency, and "8890" is the price of micro Luna in micro KRW from the voter's point of view. +where "ukrw" is the denominating currency, and "8890.0" is the price of micro Luna in micro KRW from the voter's point of view. "salt" should match the salt used to generate the SHA256 hex in the associated pre-vote. If voting from a voting delegate, set "validator" to the address of the validator to vote on behalf of: -$ terracli tx oracle vote --denom "ukrw" --price "8890" --from mykey --validator terravaloper1.... +$ terracli tx oracle vote 1234 8890.0ukrw terravaloper1.... `), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - offline := viper.GetBool(flagOffline) + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } + salt := args[0] + price, err := sdk.ParseDecCoin(args[1]) + if err != nil { + return fmt.Errorf("given price {%s} is not a valid format; price should be formatted as DecCoin", price) } // Get from address voter := cliCtx.GetFromAddress() - denom := viper.GetString(flagDenom) - priceStr := viper.GetString(flagPrice) - salt := viper.GetString(flagSalt) + denom := price.Denom + amount := price.Amount // By default the voter is voting on behalf of itself validator := sdk.ValAddress(voter) - // Override validator if flag is set - valStr := viper.GetString(flagValidator) - if len(valStr) != 0 { - parsedVal, err := sdk.ValAddressFromBech32(valStr) + // Override validator if validator is given + if len(args) == 3 { + parsedVal, err := sdk.ValAddressFromBech32(args[2]) if err != nil { return errors.Wrap(err, "validator address is invalid") } validator = parsedVal } - // Parse the price to Dec - var price sdk.Dec - if len(priceStr) == 0 { - price = sdk.ZeroDec() - } else { - var err sdk.Error - price, err = sdk.NewDecFromStr(priceStr) - if err != nil { - return fmt.Errorf("given price {%s} is not a valid format; price should be formatted as float", priceStr) - } - } - - msg := oracle.NewMsgPriceVote(price, salt, denom, voter, validator) - err := msg.ValidateBasic() + msg := types.NewMsgPriceVote(amount, salt, denom, voter, validator) + err = msg.ValidateBasic() if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - cmd.Flags().String(flagDenom, "", "denominating currency") - cmd.Flags().String(flagValidator, "", "validator on behalf of which to vote (for delegated feeders)") - cmd.Flags().String(flagPrice, "", "price of Luna in denom currency is to make provte hash; this field is required to submit prevote in case absence of hash") - cmd.Flags().String(flagSalt, "", "salt is to make prevote hash; this field is required to submit prevote in case absence of hash") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection the node can still build and sign tx") - - cmd.MarkFlagRequired(flagDenom) - cmd.MarkFlagRequired(flagPrice) - cmd.MarkFlagRequired(flagSalt) - return cmd } // GetCmdDelegateFeederPermission will create a feeder permission delegation tx and sign it with the given key. func GetCmdDelegateFeederPermission(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "set-feeder", + Use: "set-feeder [feeder]", + Args: cobra.ExactArgs(1), Short: "Delegate the permission to vote for the oracle to an address", Long: strings.TrimSpace(` Delegate the permission to vote for the oracle to an address. Delegation can keep your validator operator key offline and use a separate replaceable key online. -$ terracli tx oracle set-feeder --feeder terra1... --from mykey +$ terracli tx oracle set-feeder terra1... where "terra1..." is the address you want to delegate your voting rights to. `), RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - offline := viper.GetBool(flagOffline) - - if !offline { - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - } + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) // Get from address voter := cliCtx.GetFromAddress() @@ -245,27 +190,21 @@ where "terra1..." is the address you want to delegate your voting rights to. // The address the right is being delegated from validator := sdk.ValAddress(voter) - feederStr := viper.GetString(flagFeeder) - + feederStr := args[0] feeder, err := sdk.AccAddressFromBech32(feederStr) if err != nil { return err } - msg := oracle.NewMsgDelegateFeederPermission(validator, feeder) + msg := types.NewMsgDelegateFeederPermission(validator, feeder) err = msg.ValidateBasic() if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - cmd.Flags().String(flagFeeder, "", "account the voting right will be delegated to") - - cmd.MarkFlagRequired(flagFeeder) - return cmd } diff --git a/x/oracle/client/module_client.go b/x/oracle/client/module_client.go deleted file mode 100644 index cee05f975..000000000 --- a/x/oracle/client/module_client.go +++ /dev/null @@ -1,54 +0,0 @@ -package client - -import ( - "github.com/terra-project/core/x/oracle/client/cli" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - oracleQueryCmd := &cobra.Command{ - Use: "oracle", - Short: "Querying commands for the oracle module", - } - oracleQueryCmd.AddCommand(client.GetCommands( - cli.GetCmdQueryPrice(mc.storeKey, mc.cdc), - cli.GetCmdQueryVotes(mc.storeKey, mc.cdc), - cli.GetCmdQueryPrevotes(mc.storeKey, mc.cdc), - cli.GetCmdQueryActive(mc.storeKey, mc.cdc), - cli.GetCmdQueryParams(mc.storeKey, mc.cdc), - cli.GetCmdQueryFeederDelegation(mc.storeKey, mc.cdc), - )...) - - return oracleQueryCmd - -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - oracleTxCmd := &cobra.Command{ - Use: "oracle", - Short: "Oracle transaction subcommands", - } - - oracleTxCmd.AddCommand(client.PostCommands( - cli.GetCmdPricePrevote(mc.cdc), - cli.GetCmdPriceVote(mc.cdc), - cli.GetCmdDelegateFeederPermission(mc.cdc), - )...) - - return oracleTxCmd -} diff --git a/x/oracle/client/module_client_test.go b/x/oracle/client/module_client_test.go deleted file mode 100644 index 112478300..000000000 --- a/x/oracle/client/module_client_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "params": true, - "price": true, - "active": true, - "votes": true, - "prevotes": true, - "feeder": true, - } - - txCmdList = map[string]bool{ - "prevote": true, - "vote": true, - "set-feeder": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/oracle/client/rest/query.go b/x/oracle/client/rest/query.go index 63f811a9a..d1a91d089 100644 --- a/x/oracle/client/rest/query.go +++ b/x/oracle/client/rest/query.go @@ -4,38 +4,43 @@ import ( "fmt" "net/http" - "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/oracle/internal/types" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/gorilla/mux" ) -func registerQueryRoute(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes", RestDenom), queryPrevotesHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes/{%s}", RestDenom, RestVoter), queryPrevotesHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes", RestDenom), queryVotesHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes/{%s}", RestDenom, RestVoter), queryVotesHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/price", RestDenom), queryPriceHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/oracle/denoms/actives", queryActivesHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc("/oracle/params", queryParamsHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/feeder", RestVoter), queryFeederDelegationHandlerFn(cdc, cliCtx)).Methods("GET") +func registerQueryRoute(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes", RestDenom), queryPrevotesHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes/{%s}", RestDenom, RestVoter), queryPrevotesHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes", RestDenom), queryVotesHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes/{%s}", RestDenom, RestVoter), queryVotesHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/price", RestDenom), queryPriceHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/oracle/denoms/actives", queryActivesHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/oracle/parameters", queryParamsHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/feeder", RestVoter), queryFeederDelegationHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/voting_info", RestVoter), votingInfoHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc("/oracle/voting_infos", votingInfoHandlerListFn(cliCtx)).Methods("GET") } -func queryVotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryVotesHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) denom := vars[RestDenom] voter := vars[RestVoter] var voterAddress sdk.ValAddress - params := oracle.NewQueryVotesParams(voterAddress, denom) + params := types.NewQueryVotesParams(voterAddress, denom) if len(voter) != 0 { - voterAddress, err := sdk.ValAddressFromBech32(voter) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -44,31 +49,37 @@ func queryVotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http params.Voter = voterAddress } - bz, err := cdc.MarshalJSON(params) + bz, err := cliCtx.Codec.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryVotes), bz) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryVotes), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryPrevotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryPrevotesHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) denom := vars[RestDenom] voter := vars[RestVoter] var voterAddress sdk.ValAddress - params := oracle.NewQueryPrevotesParams(voterAddress, denom) + params := types.NewQueryPrevotesParams(voterAddress, denom) if len(voter) != 0 { @@ -80,65 +91,94 @@ func queryPrevotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) h params.Voter = voterAddress } - bz, err := cdc.MarshalJSON(params) + bz, err := cliCtx.Codec.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryPrevotes), bz) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPrevotes), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryPriceHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryPriceHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) denom := vars[RestDenom] - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", oracle.QuerierRoute, oracle.QueryPrice, denom), nil) + params := types.NewQueryPriceParams(denom) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPrice), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryActivesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryActivesHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryActive), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryActives), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryParams), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryFeederDelegationHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryFeederDelegationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) voter := vars[RestVoter] @@ -148,22 +188,89 @@ func queryFeederDelegationHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - params := oracle.NewQueryFeederDelegationParams(validator) - bz, err := cdc.MarshalJSON(params) + params := types.NewQueryFeederDelegationParams(validator) + bz, err := cliCtx.Codec.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryFeederDelegation), bz) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFeederDelegation), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } var feeder sdk.AccAddress - cdc.MustUnmarshalJSON(res, &feeder) + cliCtx.Codec.MustUnmarshalJSON(res, &feeder) + + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// http request handler to query voting info +func votingInfoHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + vars := mux.Vars(r) + valAddr, err := sdk.ValAddressFromBech32(vars["voter"]) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := types.NewQueryVotingInfoParams(valAddr) + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryVotingInfo), bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// http request handler to query voting info +func votingInfoHandlerListFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := types.NewQueryVotingInfosParams(page, limit) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryVotingInfos), bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } diff --git a/x/oracle/client/rest/rest.go b/x/oracle/client/rest/rest.go index 774d6f443..971c17187 100644 --- a/x/oracle/client/rest/rest.go +++ b/x/oracle/client/rest/rest.go @@ -2,7 +2,6 @@ package rest import ( "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" "github.com/gorilla/mux" ) @@ -14,7 +13,7 @@ const ( ) // RegisterRoutes registers oracle-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - resgisterTxRoute(cliCtx, r, cdc) - registerQueryRoute(cliCtx, r, cdc) +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + resgisterTxRoute(cliCtx, r) + registerQueryRoute(cliCtx, r) } diff --git a/x/oracle/client/rest/tx.go b/x/oracle/client/rest/tx.go index b5cbfcdcc..216d10c94 100644 --- a/x/oracle/client/rest/tx.go +++ b/x/oracle/client/rest/tx.go @@ -5,21 +5,20 @@ import ( "fmt" "net/http" - "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/oracle/internal/types" "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/gorilla/mux" ) -func resgisterTxRoute(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes", RestDenom), submitPrevoteHandlerFunction(cdc, cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes", RestDenom), submitVoteHandlerFunction(cdc, cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/feeder", RestVoter), submitDelegateHandlerFunction(cdc, cliCtx)).Methods("POST") +func resgisterTxRoute(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/prevotes", RestDenom), submitPrevoteHandlerFunction(cliCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/votes", RestDenom), submitVoteHandlerFunction(cliCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/feeder", RestVoter), submitDelegateHandlerFunction(cliCtx)).Methods("POST") } // PrevoteReq ... @@ -33,13 +32,13 @@ type PrevoteReq struct { Validator string `json:"validator"` } -func submitPrevoteHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func submitPrevoteHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) denom := vars[RestDenom] var req PrevoteReq - if !rest.ReadRESTReq(w, r, cdc, &req) { + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { return } @@ -69,7 +68,7 @@ func submitPrevoteHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) h // If hash is not given, then retrieve hash from price and salt if len(req.Hash) == 0 && (!req.Price.Equal(sdk.ZeroDec()) && len(req.Salt) > 0) { - hashBytes, err := oracle.VoteHash(req.Salt, req.Price, denom, valAddress) + hashBytes, err := types.VoteHash(req.Salt, req.Price, denom, valAddress) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -79,14 +78,14 @@ func submitPrevoteHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) h } // create the message - msg := oracle.NewMsgPricePrevote(req.Hash, denom, fromAddress, valAddress) + msg := types.NewMsgPricePrevote(req.Hash, denom, fromAddress, valAddress) err = msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) } } @@ -100,13 +99,13 @@ type VoteReq struct { Validator string `json:"validator"` } -func submitVoteHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func submitVoteHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) denom := vars[RestDenom] var req VoteReq - if !rest.ReadRESTReq(w, r, cdc, &req) { + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { return } @@ -135,14 +134,14 @@ func submitVoteHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http } // create the message - msg := oracle.NewMsgPriceVote(req.Price, req.Salt, denom, fromAddress, valAddress) + msg := types.NewMsgPriceVote(req.Price, req.Salt, denom, fromAddress, valAddress) err = msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) } } @@ -152,7 +151,7 @@ type DelegateReq struct { Feeder string `json:"feeder"` } -func submitDelegateHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func submitDelegateHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) voter := vars[RestVoter] @@ -165,7 +164,7 @@ func submitDelegateHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) } var req DelegateReq - if !rest.ReadRESTReq(w, r, cdc, &req) { + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { return } @@ -195,13 +194,13 @@ func submitDelegateHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) } // create the message - msg := oracle.NewMsgDelegateFeederPermission(valAddress, feeder) + msg := types.NewMsgDelegateFeederPermission(valAddress, feeder) err = msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) } } diff --git a/x/oracle/end_blocker.go b/x/oracle/end_blocker.go deleted file mode 100644 index 5fd9a67d0..000000000 --- a/x/oracle/end_blocker.go +++ /dev/null @@ -1,173 +0,0 @@ -package oracle - -import ( - "github.com/terra-project/core/types/util" - - "sort" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/terra-project/core/types" - "github.com/terra-project/core/x/oracle/tags" -) - -// At the end of every VotePeriod, we give out all the market swap fees collected to the -// oracle voters that voted faithfully. -func rewardPrevBallotWinners(ctx sdk.Context, k Keeper) { - // Sum weight of the claimpool - prevBallotWeightSum := sdk.ZeroInt() - k.iterateClaimPool(ctx, func(_ sdk.AccAddress, weight sdk.Int) (stop bool) { - prevBallotWeightSum = prevBallotWeightSum.Add(weight) - return false - }) - - if !prevBallotWeightSum.IsZero() { - - accmFeePool := k.GetSwapFeePool(ctx) - if !accmFeePool.Empty() { - - // Dole out rewards - var distributedFee sdk.Coins - k.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - - rewardCoins := sdk.NewCoins() - rewardeeVal := k.valset.Validator(ctx, sdk.ValAddress(recipient)) - for _, feeCoin := range accmFeePool { - rewardAmt := sdk.NewDecCoinFromCoin(feeCoin).Amount.QuoInt(prevBallotWeightSum).MulInt(weight).TruncateInt() - rewardCoins = rewardCoins.Add(sdk.NewCoins(sdk.NewCoin(feeCoin.Denom, rewardAmt))) - } - - // In case absence of the validator, we collect the rewards to fee collect keeper - if rewardeeVal != nil { - k.dk.AllocateTokensToValidator(ctx, rewardeeVal, sdk.NewDecCoins(rewardCoins)) - } else { - k.fck.AddCollectedFees(ctx, rewardCoins) - } - - distributedFee = distributedFee.Add(rewardCoins) - - return false - }) - - // move left fees to fee collect keeper - leftFee := accmFeePool.Sub(distributedFee) - if !leftFee.Empty() && leftFee.IsValid() { - k.fck.AddCollectedFees(ctx, leftFee) - } - - // Change Issuerance - for _, feeCoin := range accmFeePool { - - // never return err, but handle err for lint - err := k.mk.ChangeIssuance(ctx, feeCoin.Denom, feeCoin.Amount) - if err != nil { - panic(err) - } - } - - // Clear swap fee pool - k.clearSwapFeePool(ctx) - } - - // Clear claim and fee pool - k.clearClaimPool(ctx) - } -} - -// Calculates the median and returns it. Sets the set of voters to be rewarded, i.e. voted within -// a reasonable spread from the weighted median to the store -func tally(ctx sdk.Context, k Keeper, pb PriceBallot) sdk.Dec { - if !sort.IsSorted(pb) { - sort.Sort(pb) - } - - ballotWinners := types.ClaimPool{} - weightedMedian := pb.weightedMedian(ctx, k.valset) - rewardSpread := k.GetParams(ctx).OracleRewardBand.QuoInt64(2) - - for _, vote := range pb { - if vote.Price.GTE(weightedMedian.Sub(rewardSpread)) && vote.Price.LTE(weightedMedian.Add(rewardSpread)) { - if validator := k.valset.Validator(ctx, vote.Voter); validator != nil { - bondSize := validator.GetBondedTokens() - - ballotWinners = append(ballotWinners, types.Claim{ - Recipient: sdk.AccAddress(vote.Voter), - Weight: bondSize, - }) - } - } - } - - // add claim winners to the store - k.addClaimPool(ctx, ballotWinners) - - return weightedMedian -} - -// ballot for the asset is passing the threshold amount of voting power -func ballotIsPassing(totalBondedTokens sdk.Int, voteThreshold sdk.Dec, ballotPower sdk.Int) bool { - thresholdVotes := voteThreshold.MulInt(totalBondedTokens).RoundInt() - return ballotPower.GTE(thresholdVotes) -} - -// EndBlocker is called at the end of every block -func EndBlocker(ctx sdk.Context, k Keeper) (resTags sdk.Tags) { - params := k.GetParams(ctx) - - // Not yet time for a tally - if !util.IsPeriodLastBlock(ctx, params.VotePeriod) { - return - } - - // Reward previous ballot winners - rewardPrevBallotWinners(ctx, k) - - actives := k.getActiveDenoms(ctx) - votes := k.collectVotes(ctx) - - // Clear swap rates - for _, activeDenom := range actives { - k.deletePrice(ctx, activeDenom) - } - - totalBondedTokens := k.valset.TotalBondedTokens(ctx) - - // Iterate through votes and update prices; drop if not enough votes have been achieved. - for denom, filteredVotes := range votes { - if ballotIsPassing(totalBondedTokens, params.VoteThreshold, filteredVotes.power(ctx, k.valset)) { - - // Get weighted median prices, and faithful respondants - mod := tally(ctx, k, filteredVotes) - - // Set price to the store - k.SetLunaSwapRate(ctx, denom, mod) - - resTags = sdk.NewTags( - tags.Action, tags.ActionPriceUpdate, - tags.Denom, denom, - tags.Price, mod.String(), - ) - } else { - resTags = sdk.NewTags( - tags.Action, tags.ActionTallyDropped, - tags.Denom, denom, - ) - } - } - - // Clear all prevotes - k.iteratePrevotes(ctx, func(prevote PricePrevote) (stop bool) { - if ctx.BlockHeight() > prevote.SubmitBlock+params.VotePeriod { - k.deletePrevote(ctx, prevote) - } - - return false - }) - - // Clear all votes - k.iterateVotes(ctx, func(vote PriceVote) (stop bool) { - k.deleteVote(ctx, vote) - return false - }) - - return -} diff --git a/x/oracle/end_blocker_test.go b/x/oracle/end_blocker_test.go deleted file mode 100644 index 85de35c36..000000000 --- a/x/oracle/end_blocker_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package oracle - -import ( - "encoding/hex" - "math" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - "github.com/terra-project/core/types/assets" -) - -func TestOracleThreshold(t *testing.T) { - input, h := setup(t) - - // Less than the threshold signs, msg fails - // Prevote without price - salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - res := h(input.ctx.WithBlockHeight(0), prevoteMsg) - require.True(t, res.IsOK()) - - // Vote and new Prevote - voteMsg := NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - res = h(input.ctx.WithBlockHeight(1), voteMsg) - require.True(t, res.IsOK()) - - EndBlocker(input.ctx.WithBlockHeight(1), input.oracleKeeper) - - _, err = input.oracleKeeper.GetLunaSwapRate(input.ctx.WithBlockHeight(1), assets.MicroSDRDenom) - require.NotNil(t, err) - - // More than the threshold signs, msg succeeds - salt = "1" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - salt = "2" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - salt = "3" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[2])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[2], sdk.ValAddress(addrs[2])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[2], sdk.ValAddress(addrs[2])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - EndBlocker(input.ctx.WithBlockHeight(1), input.oracleKeeper) - - price, err := input.oracleKeeper.GetLunaSwapRate(input.ctx.WithBlockHeight(1), assets.MicroSDRDenom) - require.Nil(t, err) - require.Equal(t, randomPrice, price) - - // Less than the threshold signs, msg fails - val, _ := input.stakingKeeper.GetValidator(input.ctx, sdk.ValAddress(addrs[2])) - input.stakingKeeper.Delegate(input.ctx.WithBlockHeight(0), addrs[2], uLunaAmt.MulRaw(2), val, false) - - salt = "1" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - salt = "2" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - EndBlocker(input.ctx.WithBlockHeight(1), input.oracleKeeper) - - price, err = input.oracleKeeper.GetLunaSwapRate(input.ctx.WithBlockHeight(1), assets.MicroSDRDenom) - require.NotNil(t, err) -} - -func TestOracleMultiVote(t *testing.T) { - input, h := setup(t) - - // Less than the threshold signs, msg fails - salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - res := h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - res = h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[2])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[2], sdk.ValAddress(addrs[2])) - res = h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - bz, err = VoteHash(salt, anotherRandomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - bz, err = VoteHash(salt, anotherRandomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - res = h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - bz, err = VoteHash(salt, anotherRandomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[2])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[2], sdk.ValAddress(addrs[2])) - res = h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - // Reveal Price - input.ctx = input.ctx.WithBlockHeight(1) - voteMsg := NewMsgPriceVote(anotherRandomPrice, salt, assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - res = h(input.ctx, voteMsg) - require.True(t, res.IsOK()) - - voteMsg = NewMsgPriceVote(anotherRandomPrice, salt, assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - res = h(input.ctx, voteMsg) - require.True(t, res.IsOK()) - - voteMsg = NewMsgPriceVote(anotherRandomPrice, salt, assets.MicroSDRDenom, addrs[2], sdk.ValAddress(addrs[2])) - res = h(input.ctx, voteMsg) - require.True(t, res.IsOK()) - - EndBlocker(input.ctx, input.oracleKeeper) - - price, err := input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroSDRDenom) - require.Nil(t, err) - require.Equal(t, price, anotherRandomPrice) -} - -func TestOracleDrop(t *testing.T) { - input, h := setup(t) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, randomPrice) - - salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroKRWDenom, sdk.ValAddress(addrs[0])) - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroKRWDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx, prevoteMsg) - - input.ctx = input.ctx.WithBlockHeight(1) - voteMsg := NewMsgPriceVote(randomPrice, salt, assets.MicroKRWDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx, voteMsg) - - // Immediately swap halt after an illiquid oracle vote - EndBlocker(input.ctx, input.oracleKeeper) - - _, err = input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroKRWDenom) - require.NotNil(t, err) -} - -func TestOracleTally(t *testing.T) { - input, _ := setup(t) - - ballot := PriceBallot{} - prices, valAccAddrs, mockValset := generateRandomTestCase() - input.oracleKeeper.valset = mockValset - h := NewHandler(input.oracleKeeper) - for i, price := range prices { - - decPrice := sdk.NewDecWithPrec(int64(price*math.Pow10(oracleDecPrecision)), int64(oracleDecPrecision)) - - salt := string(i) - bz, err := VoteHash(salt, decPrice, assets.MicroSDRDenom, sdk.ValAddress(valAccAddrs[i])) - require.Nil(t, err) - - prevoteMsg := NewMsgPricePrevote( - hex.EncodeToString(bz), - assets.MicroSDRDenom, - valAccAddrs[i], - sdk.ValAddress(valAccAddrs[i]), - ) - - res := h(input.ctx.WithBlockHeight(0), prevoteMsg) - require.True(t, res.IsOK()) - - voteMsg := NewMsgPriceVote( - decPrice, - salt, - assets.MicroSDRDenom, - valAccAddrs[i], - sdk.ValAddress(valAccAddrs[i]), - ) - - res = h(input.ctx.WithBlockHeight(1), voteMsg) - require.True(t, res.IsOK()) - - vote := NewPriceVote(decPrice, assets.MicroSDRDenom, sdk.ValAddress(valAccAddrs[i])) - ballot = append(ballot, vote) - - // change power of every three validator - if i%3 == 0 { - mockValset.Validators[i].Power = sdk.NewInt(int64(i + 1)) - } - } - - rewardees := []sdk.AccAddress{} - weightedMedian := ballot.weightedMedian(input.ctx, mockValset) - maxSpread := input.oracleKeeper.GetParams(input.ctx).OracleRewardBand.QuoInt64(2) - - for _, vote := range ballot { - if vote.Price.GTE(weightedMedian.Sub(maxSpread)) && vote.Price.LTE(weightedMedian.Add(maxSpread)) { - rewardees = append(rewardees, sdk.AccAddress(vote.Voter)) - } - } - - tallyMedian := tally(input.ctx, input.oracleKeeper, ballot) - - require.Equal(t, countClaimPool(input.ctx, input.oracleKeeper), len(rewardees)) - require.Equal(t, tallyMedian.MulInt64(100).TruncateInt(), weightedMedian.MulInt64(100).TruncateInt()) -} - -func TestOracleTallyTiming(t *testing.T) { - input, h := setup(t) - - // all the addrs vote for the block ... not last period block yet, so tally fails - for _, addr := range addrs { - salt := "1" - bz, err := VoteHash(salt, sdk.OneDec(), assets.MicroSDRDenom, sdk.ValAddress(addr)) - require.Nil(t, err) - - prevoteMsg := NewMsgPricePrevote( - hex.EncodeToString(bz), - assets.MicroSDRDenom, - addr, - sdk.ValAddress(addr), - ) - - res := h(input.ctx, prevoteMsg) - require.True(t, res.IsOK()) - - voteMsg := NewMsgPriceVote( - sdk.OneDec(), - salt, - assets.MicroSDRDenom, - addr, - sdk.ValAddress(addr), - ) - - res = h(input.ctx.WithBlockHeight(1), voteMsg) - require.True(t, res.IsOK()) - } - - params := input.oracleKeeper.GetParams(input.ctx) - params.VotePeriod = 10 // set vote period to 10 for now, for convinience - input.oracleKeeper.SetParams(input.ctx, params) - require.Equal(t, 0, int(input.ctx.BlockHeight())) - - EndBlocker(input.ctx, input.oracleKeeper) - require.Equal(t, 0, countClaimPool(input.ctx, input.oracleKeeper)) - - input.ctx = input.ctx.WithBlockHeight(params.VotePeriod - 1) - - EndBlocker(input.ctx, input.oracleKeeper) - require.Equal(t, len(addrs), countClaimPool(input.ctx, input.oracleKeeper)) -} - -func countClaimPool(ctx sdk.Context, oracleKeeper Keeper) (claimCount int) { - oracleKeeper.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - claimCount++ - return false - }) - - return claimCount -} - -func TestOracleRewardDistribution(t *testing.T) { - input, h := setup(t) - - salt := "1" - bz, _ := VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg := NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - salt = "2" - bz, _ = VoteHash(salt, randomPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])) - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(0), prevoteMsg) - - voteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[1], sdk.ValAddress(addrs[1])) - h(input.ctx.WithBlockHeight(1), voteMsg) - - input.oracleKeeper.AddSwapFeePool(input.ctx.WithBlockHeight(1), sdk.NewCoins(sdk.NewCoin(assets.MicroSDRDenom, uLunaAmt.MulRaw(100)))) - - EndBlocker(input.ctx.WithBlockHeight(1), input.oracleKeeper) - EndBlocker(input.ctx.WithBlockHeight(2), input.oracleKeeper) - - rewards := input.distrKeeper.GetValidatorOutstandingRewards(input.ctx.WithBlockHeight(2), sdk.ValAddress(addrs[0])) - require.Equal(t, uLunaAmt.MulRaw(50), rewards.AmountOf(assets.MicroSDRDenom).TruncateInt()) - rewards = input.distrKeeper.GetValidatorOutstandingRewards(input.ctx.WithBlockHeight(2), sdk.ValAddress(addrs[1])) - require.Equal(t, uLunaAmt.MulRaw(50), rewards.AmountOf(assets.MicroSDRDenom).TruncateInt()) -} diff --git a/x/oracle/expected_keepers.go b/x/oracle/expected_keepers.go deleted file mode 100644 index 25216c1a9..000000000 --- a/x/oracle/expected_keepers.go +++ /dev/null @@ -1,18 +0,0 @@ -package oracle - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// expected coin keeper -type DistributionKeeper interface { - AllocateTokensToValidator(ctx sdk.Context, val sdk.Validator, tokens sdk.DecCoins) -} - -// expected fee keeper -type FeeCollectionKeeper interface { - AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins -} - -// expected mint keeper -type MintKeeper interface { - ChangeIssuance(ctx sdk.Context, denom string, delta sdk.Int) (err sdk.Error) -} diff --git a/x/oracle/genesis.go b/x/oracle/genesis.go index c16cf7ecd..5b5195795 100644 --- a/x/oracle/genesis.go +++ b/x/oracle/genesis.go @@ -4,39 +4,52 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// GenesisState - all oracle state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params"` // oracle params -} +// InitGenesis initialize default parameters +// and the keeper's address to pubkey map +func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { -// NewGenesisState creates new oracle GenesisState -func NewGenesisState(params Params) GenesisState { - return GenesisState{ - Params: params, + for addr, info := range data.VotingInfos { + address, err := sdk.ValAddressFromBech32(addr) + if err != nil { + panic(err) + } + keeper.SetVotingInfo(ctx, address, info) } -} -// DefaultGenesisState get raw genesis raw message for testing -func DefaultGenesisState() GenesisState { - return GenesisState{ - Params: DefaultParams(), + for addr, array := range data.MissedVotes { + address, err := sdk.ValAddressFromBech32(addr) + if err != nil { + panic(err) + } + for _, missed := range array { + keeper.SetMissedVoteBitArray(ctx, address, missed.Index, missed.Missed) + } } -} -// InitGenesis creates new oracle genesis -func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetParams(ctx, data.Params) } -// ExportGenesis returns a GenesisState for a given context and keeper. The -// GenesisState will contain the pool, and validator/delegator distribution info's -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { +// ExportGenesis writes the current store values +// to a genesis file, which can be imported again +// with InitGenesis +func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { params := keeper.GetParams(ctx) - return NewGenesisState(params) -} + votingInfos := make(map[string]VotingInfo) + missedVotes := make(map[string][]MissedVote) + keeper.IterateVotingInfos(ctx, func(info VotingInfo) (stop bool) { + bechAddr := info.Address.String() + + votingInfos[bechAddr] = info + localMissedBlocks := []MissedVote{} + + keeper.IterateMissedVoteBitArray(ctx, info.Address, func(index int64, missed bool) (stop bool) { + localMissedBlocks = append(localMissedBlocks, NewMissedVote(index, missed)) + return false + }) + missedVotes[bechAddr] = localMissedBlocks + + return false + }) -// ValidateGenesis validates the provided oracle genesis state to ensure the -// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) -func ValidateGenesis(data GenesisState) error { - return validateParams(data.Params) + return NewGenesisState(params, votingInfos, missedVotes) } diff --git a/x/oracle/handler.go b/x/oracle/handler.go index e0ff3ac7e..ef5e268ff 100644 --- a/x/oracle/handler.go +++ b/x/oracle/handler.go @@ -3,11 +3,12 @@ package oracle import ( "bytes" "encoding/hex" - - "github.com/terra-project/core/x/oracle/tags" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" + + "github.com/terra-project/core/x/oracle/internal/types" ) // NewHandler returns a handler for "oracle" type messages. @@ -21,7 +22,7 @@ func NewHandler(k Keeper) sdk.Handler { case MsgDelegateFeederPermission: return handleMsgDelegateFeederPermission(ctx, k, msg) default: - errMsg := "Unrecognized oracle Msg type: %s" + msg.Type() + errMsg := fmt.Sprintf("Unrecognized oracle message type: %T", msg) return sdk.ErrUnknownRequest(errMsg).Result() } } @@ -29,110 +30,122 @@ func NewHandler(k Keeper) sdk.Handler { // handleMsgPricePrevote handles a MsgPricePrevote func handleMsgPricePrevote(ctx sdk.Context, keeper Keeper, ppm MsgPricePrevote) sdk.Result { - valset := keeper.valset - if !ppm.Feeder.Equals(ppm.Validator) { delegate := keeper.GetFeedDelegate(ctx, ppm.Validator) if !delegate.Equals(ppm.Feeder) { - return ErrNoVotingPermission(DefaultCodespace, ppm.Feeder, ppm.Validator).Result() + return ErrNoVotingPermission(keeper.Codespace(), ppm.Feeder, ppm.Validator).Result() } } // Check that the given validator exists - val := valset.Validator(ctx, ppm.Validator) + val := keeper.StakingKeeper.Validator(ctx, ppm.Validator) if val == nil { - return staking.ErrNoValidatorFound(DefaultCodespace).Result() + return staking.ErrNoValidatorFound(keeper.Codespace()).Result() } prevote := NewPricePrevote(ppm.Hash, ppm.Denom, ppm.Validator, ctx.BlockHeight()) - keeper.addPrevote(ctx, prevote) - - return sdk.Result{ - Tags: sdk.NewTags( - tags.Denom, ppm.Denom, - tags.Voter, ppm.Validator.String(), - tags.FeedDelegate, ppm.Feeder.String(), + keeper.AddPrevote(ctx, prevote) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypePrevote, + sdk.NewAttribute(types.AttributeKeyDenom, ppm.Denom), + sdk.NewAttribute(types.AttributeKeyVoter, ppm.Validator.String()), + sdk.NewAttribute(types.AttributeKeyFeeder, ppm.Feeder.String()), ), - } + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } // handleMsgPriceVote handles a MsgPriceVote func handleMsgPriceVote(ctx sdk.Context, keeper Keeper, pvm MsgPriceVote) sdk.Result { - valset := keeper.valset - if !pvm.Feeder.Equals(pvm.Validator) { delegate := keeper.GetFeedDelegate(ctx, pvm.Validator) if !delegate.Equals(pvm.Feeder) { - return ErrNoVotingPermission(DefaultCodespace, pvm.Feeder, pvm.Validator).Result() + return ErrNoVotingPermission(keeper.Codespace(), pvm.Feeder, pvm.Validator).Result() } } // Check that the given validator exists - val := valset.Validator(ctx, pvm.Validator) + val := keeper.StakingKeeper.Validator(ctx, pvm.Validator) if val == nil { - return staking.ErrNoValidatorFound(DefaultCodespace).Result() + return staking.ErrNoValidatorFound(keeper.Codespace()).Result() } params := keeper.GetParams(ctx) // Get prevote - prevote, err := keeper.getPrevote(ctx, pvm.Denom, pvm.Validator) + prevote, err := keeper.GetPrevote(ctx, pvm.Denom, pvm.Validator) if err != nil { - return ErrNoPrevote(DefaultCodespace, pvm.Validator, pvm.Denom).Result() + return ErrNoPrevote(keeper.Codespace(), pvm.Validator, pvm.Denom).Result() } // Check a msg is submitted porper period if (ctx.BlockHeight()/params.VotePeriod)-(prevote.SubmitBlock/params.VotePeriod) != 1 { - return ErrNotRevealPeriod(DefaultCodespace).Result() + return ErrNotRevealPeriod(keeper.Codespace()).Result() } // If there is an prevote, we verify a price with prevote hash and move prevote to vote with given price bz, _ := hex.DecodeString(prevote.Hash) // prevote hash bz2, err2 := VoteHash(pvm.Salt, pvm.Price, prevote.Denom, prevote.Voter) if err2 != nil { - return ErrVerificationFailed(DefaultCodespace, bz, []byte{}).Result() + return ErrVerificationFailed(keeper.Codespace(), bz, []byte{}).Result() } if !bytes.Equal(bz, bz2) { - return ErrVerificationFailed(DefaultCodespace, bz, bz2).Result() + return ErrVerificationFailed(keeper.Codespace(), bz, bz2).Result() } // Add the vote to the store vote := NewPriceVote(pvm.Price, prevote.Denom, prevote.Voter) - keeper.deletePrevote(ctx, prevote) - keeper.addVote(ctx, vote) - - log := NewLog() - log = log.append(LogKeyPrice, pvm.Price.String()) - - return sdk.Result{ - Tags: sdk.NewTags( - tags.Denom, pvm.Denom, - tags.Voter, pvm.Validator.String(), - tags.FeedDelegate, pvm.Feeder.String(), + keeper.DeletePrevote(ctx, prevote) + keeper.AddVote(ctx, vote) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeVote, + sdk.NewAttribute(types.AttributeKeyDenom, pvm.Denom), + sdk.NewAttribute(types.AttributeKeyVoter, pvm.Validator.String()), + sdk.NewAttribute(types.AttributeKeyFeeder, pvm.Feeder.String()), ), - Log: log.String(), - } + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } // handleMsgDelegateFeederPermission handles a MsgDelegateFeederPermission func handleMsgDelegateFeederPermission(ctx sdk.Context, keeper Keeper, dfpm MsgDelegateFeederPermission) sdk.Result { - valset := keeper.valset signer := dfpm.Operator // Check the delegator is a validator - val := valset.Validator(ctx, signer) + val := keeper.StakingKeeper.Validator(ctx, signer) if val == nil { - return staking.ErrNoValidatorFound(DefaultCodespace).Result() + return staking.ErrNoValidatorFound(keeper.Codespace()).Result() } // Set the delegation - keeper.SetFeedDelegate(ctx, signer, dfpm.FeedDelegate) + keeper.SetFeedDelegate(ctx, signer, dfpm.Delegatee) - return sdk.Result{ - Tags: sdk.NewTags( - tags.Operator, dfpm.Operator.String(), - tags.FeedDelegate, dfpm.FeedDelegate.String(), + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeFeedDeleate, + sdk.NewAttribute(types.AttributeKeyOperator, dfpm.Operator.String()), + sdk.NewAttribute(types.AttributeKeyFeeder, dfpm.Delegatee.String()), ), - } + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} } diff --git a/x/oracle/handler_test.go b/x/oracle/handler_test.go index f53d047ac..eeb5b414b 100644 --- a/x/oracle/handler_test.go +++ b/x/oracle/handler_test.go @@ -4,14 +4,14 @@ import ( "encoding/hex" "testing" - "github.com/terra-project/core/types/assets" + "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" - cosmock "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/cosmos/cosmos-sdk/x/mock" - "github.com/stretchr/testify/require" + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/keeper" ) func TestOracleFilters(t *testing.T) { @@ -19,30 +19,30 @@ func TestOracleFilters(t *testing.T) { // Case 1: non-oracle message being sent fails bankMsg := bank.MsgSend{} - res := h(input.ctx, bankMsg) + res := h(input.Ctx, bankMsg) require.False(t, res.IsOK()) // Case 2: Normal MsgPricePrevote submission goes through salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroSDRDenom, types.ValAddress(addrs[0])) + bz, err := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) require.Nil(t, err) - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], types.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) require.True(t, res.IsOK()) - // Case 3: Normal MsgPriceVote submission goes through - voteMsg := NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, addrs[0], types.ValAddress(addrs[0])) - res = h(input.ctx.WithBlockHeight(1), voteMsg) + // // Case 3: Normal MsgPriceVote submission goes through keeper.keeper.Addrs + voteMsg := NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx.WithBlockHeight(1), voteMsg) require.True(t, res.IsOK()) // Case 4: a non-validator sending an oracle message fails - _, randoAddrs := cosmock.GeneratePrivKeyAddressPairs(1) + _, addrs := mock.GeneratePrivKeyAddressPairs(1) salt = "2" - bz, err = VoteHash(salt, randomPrice, assets.MicroSDRDenom, types.ValAddress(randoAddrs[0])) + bz, err = VoteHash(salt, randomPrice, core.MicroSDRDenom, sdk.ValAddress(addrs[0])) require.Nil(t, err) - prevoteMsg = NewMsgPricePrevote("", assets.MicroSDRDenom, randoAddrs[0], types.ValAddress(randoAddrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg = NewMsgPricePrevote("", core.MicroSDRDenom, addrs[0], sdk.ValAddress(addrs[0])) + res = h(input.Ctx, prevoteMsg) require.False(t, res.IsOK()) } @@ -50,27 +50,27 @@ func TestPrevoteCheck(t *testing.T) { input, h := setup(t) salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroSDRDenom, types.ValAddress(addrs[0])) + bz, err := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) require.Nil(t, err) - pricePrevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], types.ValAddress(addrs[0])) - res := h(input.ctx, pricePrevoteMsg) + pricePrevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res := h(input.Ctx, pricePrevoteMsg) require.True(t, res.IsOK()) // Invalid price reveal period - priceVoteMsg := NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, sdk.AccAddress(addrs[0]), types.ValAddress(addrs[0])) - res = h(input.ctx, priceVoteMsg) + priceVoteMsg := NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, sdk.AccAddress(keeper.Addrs[0]), keeper.ValAddrs[0]) + res = h(input.Ctx, priceVoteMsg) require.False(t, res.IsOK()) - input.ctx = input.ctx.WithBlockHeight(2) - priceVoteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, sdk.AccAddress(addrs[0]), types.ValAddress(addrs[0])) - res = h(input.ctx, priceVoteMsg) + input.Ctx = input.Ctx.WithBlockHeight(2) + priceVoteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, sdk.AccAddress(keeper.Addrs[0]), keeper.ValAddrs[0]) + res = h(input.Ctx, priceVoteMsg) require.False(t, res.IsOK()) // valid price reveal submission - input.ctx = input.ctx.WithBlockHeight(1) - priceVoteMsg = NewMsgPriceVote(randomPrice, salt, assets.MicroSDRDenom, sdk.AccAddress(addrs[0]), types.ValAddress(addrs[0])) - res = h(input.ctx, priceVoteMsg) + input.Ctx = input.Ctx.WithBlockHeight(1) + priceVoteMsg = NewMsgPriceVote(randomPrice, salt, core.MicroSDRDenom, sdk.AccAddress(keeper.Addrs[0]), keeper.ValAddrs[0]) + res = h(input.Ctx, priceVoteMsg) require.True(t, res.IsOK()) } @@ -79,36 +79,36 @@ func TestFeederDelegation(t *testing.T) { input, h := setup(t) salt := "1" - bz, err := VoteHash(salt, randomPrice, assets.MicroSDRDenom, types.ValAddress(addrs[0])) + bz, err := VoteHash(salt, randomPrice, core.MicroSDRDenom, keeper.ValAddrs[0]) require.Nil(t, err) // Case 1: empty message bankMsg := MsgDelegateFeederPermission{} - res := h(input.ctx, bankMsg) + res := h(input.Ctx, bankMsg) require.False(t, res.IsOK()) // Case 2: Normal Prevote - without delegation - prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[0], types.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg := NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[0], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) require.True(t, res.IsOK()) // Case 2.1: Normal Prevote - with delegation fails - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], types.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) require.False(t, res.IsOK()) // Case 3: Normal MsgDelegateFeederPermission succeeds - msg := NewMsgDelegateFeederPermission(types.ValAddress(addrs[0]), addrs[1]) - res = h(input.ctx, msg) + msg := NewMsgDelegateFeederPermission(keeper.ValAddrs[0], keeper.Addrs[1]) + res = h(input.Ctx, msg) require.True(t, res.IsOK()) // Case 4: Normal Prevote - without delegation fails - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[2], types.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[2], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) require.False(t, res.IsOK()) // Case 5: Normal Prevote - with delegation succeeds - prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), assets.MicroSDRDenom, addrs[1], types.ValAddress(addrs[0])) - res = h(input.ctx, prevoteMsg) + prevoteMsg = NewMsgPricePrevote(hex.EncodeToString(bz), core.MicroSDRDenom, keeper.Addrs[1], keeper.ValAddrs[0]) + res = h(input.Ctx, prevoteMsg) require.True(t, res.IsOK()) } diff --git a/x/oracle/internal/keeper/hooks.go b/x/oracle/internal/keeper/hooks.go new file mode 100644 index 000000000..cec4aecce --- /dev/null +++ b/x/oracle/internal/keeper/hooks.go @@ -0,0 +1,49 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// AfterValidatorBonded register voting info for a validator +func (k Keeper) AfterValidatorBonded(ctx sdk.Context, _ sdk.ConsAddress, address sdk.ValAddress) { + // Update the signing info start height or create a new signing info + _, found := k.getVotingInfo(ctx, address) + if !found { + votingInfo := types.NewVotingInfo( + address, + ctx.BlockHeight(), + 0, + 0, + ) + k.SetVotingInfo(ctx, address, votingInfo) + } +} + +// Hooks wrapper struct for slashing keeper +type Hooks struct { + k Keeper +} + +var _ types.StakingHooks = Hooks{} + +// Return the wrapper struct +func (k Keeper) Hooks() Hooks { + return Hooks{k} +} + +// Implements sdk.ValidatorHooks +func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + h.k.AfterValidatorBonded(ctx, consAddr, valAddr) +} + +// nolint - unused hooks +func (h Hooks) AfterValidatorRemoved(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} +func (h Hooks) AfterValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} +func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} +func (h Hooks) BeforeDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) BeforeDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) AfterDelegationModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} +func (h Hooks) BeforeValidatorSlashed(_ sdk.Context, _ sdk.ValAddress, _ sdk.Dec) {} diff --git a/x/oracle/internal/keeper/infractions.go b/x/oracle/internal/keeper/infractions.go new file mode 100644 index 000000000..1e8c76f18 --- /dev/null +++ b/x/oracle/internal/keeper/infractions.go @@ -0,0 +1,113 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// HandleWrongVotes handles a wrong votes, must be called once per validator per voting period. +func (k Keeper) HandleBallotAttendees(ctx sdk.Context, ballotAttendees map[string]bool) { + for addr, valid := range ballotAttendees { + valAddr, err := sdk.ValAddressFromBech32(addr) + if err != nil { + panic(err) // NOTE never occurs + } + + k.handleBallotAttendee(ctx, valAddr, valid) + } +} + +func (k Keeper) handleBallotAttendee(ctx sdk.Context, valAddr sdk.ValAddress, valid bool) { + logger := k.Logger(ctx) + height := ctx.BlockHeight() + + // fetch voting info + votingInfo, found := k.getVotingInfo(ctx, valAddr) + if !found { + panic(fmt.Sprintf("Expected signing info for validator %s but not found", valAddr)) + } + + // this is a relative index, so it counts blocks the validator *should* have signed + // will use the 0-value default signing info if not present, except for start height + index := votingInfo.IndexOffset % k.VotesWindow(ctx) + votingInfo.IndexOffset++ + + // Update signed block bit array & counter + // This counter just tracks the sum of the bit array + // That way we avoid needing to read/write the whole array each time + previous := k.GetMissedVoteBitArray(ctx, valAddr, index) + missed := !valid + switch { + case !previous && missed: + // Array value has changed from not missed to missed, increment counter + k.SetMissedVoteBitArray(ctx, valAddr, index, true) + votingInfo.MissedVotesCounter++ + case previous && !missed: + // Array value has changed from missed to not missed, decrement counter + k.SetMissedVoteBitArray(ctx, valAddr, index, false) + votingInfo.MissedVotesCounter-- + default: + // Array value at this index has not changed, no need to update counter + } + + if missed { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeLiveness, + sdk.NewAttribute(types.AttributeKeyAddress, valAddr.String()), + sdk.NewAttribute(types.AttributeKeyMissedVotes, fmt.Sprintf("%d", votingInfo.MissedVotesCounter)), + sdk.NewAttribute(types.AttributeKeyHeight, fmt.Sprintf("%d", height)), + ), + ) + + logger.Info( + fmt.Sprintf("Wrong voting validator %s at height %d, %d missed, threshold %d", valAddr, height, votingInfo.MissedVotesCounter, k.MinValidVotesPerWindow(ctx))) + } + + minHeight := votingInfo.StartHeight + k.VotesWindow(ctx) + maxMissed := k.VotesWindow(ctx) - k.MinValidVotesPerWindow(ctx) + + // if we are past the minimum height and the validator has missed too many blocks, punish them + if height > minHeight && votingInfo.MissedVotesCounter > maxMissed { + validator := k.StakingKeeper.Validator(ctx, valAddr) + if validator != nil && !validator.IsJailed() { + power := validator.GetConsensusPower() + + // Downtime confirmed: slash and jail the validator + logger.Info(fmt.Sprintf("Validator %s past min height of %d and below valid blocks threshold of %d", + valAddr, minHeight, k.MinValidVotesPerWindow(ctx))) + + // We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height, + // and subtract an additional 1 since this is the LastCommit. + // Note that this *can* result in a negative "distributionHeight" up to -ValidatorUpdateDelay-1, + // i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block. + // That's fine since this is just used to filter unbonding delegations & redelegations. + distributionHeight := height - sdk.ValidatorUpdateDelay - 1 + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSlash, + sdk.NewAttribute(types.AttributeKeyAddress, valAddr.String()), + sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), + ), + ) + + k.StakingKeeper.Slash(ctx, validator.GetConsAddr(), distributionHeight, power, k.SlashFraction(ctx)) + + // We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding. + votingInfo.MissedVotesCounter = 0 + votingInfo.IndexOffset = 0 + k.clearMissedVoteBitArray(ctx, valAddr) + } else { + // Validator was (a) not found or (b) already jailed, don't slash + logger.Info( + fmt.Sprintf("Validator %s would have been slashed for invalid oracle votes, but was either not found in store or already jailed", valAddr), + ) + } + } + + // Set the updated signing info + k.SetVotingInfo(ctx, valAddr, votingInfo) +} diff --git a/x/oracle/internal/keeper/infractions_test.go b/x/oracle/internal/keeper/infractions_test.go new file mode 100644 index 000000000..566144ea7 --- /dev/null +++ b/x/oracle/internal/keeper/infractions_test.go @@ -0,0 +1,110 @@ +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +// Test a new validator entering the validator set +// Ensure that VotingInfo.StartHeight is set correctly +func TestHandleNewValidator(t *testing.T) { + // initial setup + input := CreateTestInput(t) + addr, val := ValAddrs[0], PubKeys[0] + amt := sdk.TokensFromConsensusPower(100) + sh := staking.NewHandler(input.StakingKeeper) + + // 1000 first blocks not a validator + ctx := input.Ctx.WithBlockHeight(input.OracleKeeper.VotesWindow(input.Ctx) + 1) + + // Validator created + got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, got.IsOK()) + staking.EndBlocker(ctx, input.StakingKeeper) + + require.Equal( + t, input.BankKeeper.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(input.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + ) + require.Equal(t, amt, input.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) + + // Now a validator, for two blocks + ballotAttendees := make(map[string]bool) + ballotAttendees[addr.String()] = true + input.OracleKeeper.HandleBallotAttendees(ctx, ballotAttendees) + + ctx = ctx.WithBlockHeight(input.OracleKeeper.VotesWindow(ctx) + 2) + ballotAttendees[addr.String()] = false + input.OracleKeeper.HandleBallotAttendees(ctx, ballotAttendees) + + info, found := input.OracleKeeper.getVotingInfo(ctx, addr) + require.True(t, found) + require.Equal(t, input.OracleKeeper.VotesWindow(ctx)+1, info.StartHeight) + require.Equal(t, int64(2), info.IndexOffset) + require.Equal(t, int64(1), info.MissedVotesCounter) + + // validator should be bonded still, should not have been slashed + validator := input.StakingKeeper.Validator(ctx, addr) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + bondPool := input.StakingKeeper.GetBondedPool(ctx) + expTokens := sdk.TokensFromConsensusPower(100) + require.Equal(t, expTokens.Int64(), bondPool.GetCoins().AmountOf(input.StakingKeeper.BondDenom(ctx)).Int64()) +} + +// Test slahsing a validator who did more than 5% wrong votes in VotingWindow +func TestSlash(t *testing.T) { + // initial setup + input := CreateTestInput(t) + addr, val := ValAddrs[0], PubKeys[0] + amt := sdk.TokensFromConsensusPower(100) + sk := input.StakingKeeper + sh := staking.NewHandler(sk) + ctx := input.Ctx + + got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, got.IsOK()) + staking.EndBlocker(ctx, sk) + + height := int64(0) + for ; height <= int64(50); height++ { + ctx = ctx.WithBlockHeight(height) + + ballotAttendees := make(map[string]bool) + ballotAttendees[addr.String()] = true + input.OracleKeeper.HandleBallotAttendees(ctx, ballotAttendees) + } + + // shouldn't be slashed + validator := sk.Validator(ctx, addr) + expTokens := sdk.TokensFromConsensusPower(100) + require.Equal(t, expTokens.Int64(), validator.GetBondedTokens().Int64()) + + // missed 95% blocks + for ; height <= int64(1000); height++ { + ctx = ctx.WithBlockHeight(height) + + ballotAttendees := make(map[string]bool) + ballotAttendees[addr.String()] = false + input.OracleKeeper.HandleBallotAttendees(ctx, ballotAttendees) + } + + // shouldn't be slashed + validator = sk.Validator(ctx, addr) + require.Equal(t, expTokens.Int64(), validator.GetBondedTokens().Int64()) + + for ; height <= int64(1001); height++ { + ctx = ctx.WithBlockHeight(height) + + ballotAttendees := make(map[string]bool) + ballotAttendees[addr.String()] = false + input.OracleKeeper.HandleBallotAttendees(ctx, ballotAttendees) + } + + validator = sk.Validator(ctx, addr) + slashFraction := input.OracleKeeper.SlashFraction(ctx) + require.Equal(t, sdk.OneDec().Sub(slashFraction).MulInt(expTokens).TruncateInt(), validator.GetBondedTokens()) +} diff --git a/x/oracle/internal/keeper/keeper.go b/x/oracle/internal/keeper/keeper.go new file mode 100644 index 000000000..63f1237f9 --- /dev/null +++ b/x/oracle/internal/keeper/keeper.go @@ -0,0 +1,406 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// Keeper of the oracle store +type Keeper struct { + cdc *codec.Codec + storeKey sdk.StoreKey + paramSpace params.Subspace + + distrKeeper types.DistributionKeeper + StakingKeeper types.StakingKeeper + supplyKeeper types.SupplyKeeper + + distrName string + + // codespace + codespace sdk.CodespaceType +} + +// NewKeeper constructs a new keeper for oracle +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, + paramspace params.Subspace, distrKeeper types.DistributionKeeper, + stakingKeeper types.StakingKeeper, supplyKeeper types.SupplyKeeper, + distrName string, codespace sdk.CodespaceType) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + paramSpace: paramspace.WithKeyTable(ParamKeyTable()), + distrKeeper: distrKeeper, + StakingKeeper: stakingKeeper, + supplyKeeper: supplyKeeper, + distrName: distrName, + codespace: codespace, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// Codespace returns a codespace of keeper +func (k Keeper) Codespace() sdk.CodespaceType { + return k.codespace +} + +//----------------------------------- +// Prevote logic + +// Iterate over prevotes in the store +func (k Keeper) IteratePrevotes(ctx sdk.Context, handler func(prevote types.PricePrevote) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.PrevoteKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + var prevote types.PricePrevote + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &prevote) + if handler(prevote) { + break + } + } +} + +// Iterate over votes in the store +func (k Keeper) iteratePrevotesWithPrefix(ctx sdk.Context, prefix []byte, handler func(vote types.PricePrevote) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + var prevote types.PricePrevote + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &prevote) + if handler(prevote) { + break + } + } +} + +//----------------------------------- +// Votes logic + +// collectVotes collects all oracle votes for the period, categorized by the votes' denom parameter +func (k Keeper) CollectVotes(ctx sdk.Context) (votes map[string]types.PriceBallot) { + votes = map[string]types.PriceBallot{} + handler := func(vote types.PriceVote) (stop bool) { + votes[vote.Denom] = append(votes[vote.Denom], vote) + return false + } + k.IterateVotes(ctx, handler) + + return +} + +// Iterate over votes in the store +func (k Keeper) IterateVotes(ctx sdk.Context, handler func(vote types.PriceVote) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.VoteKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + var vote types.PriceVote + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vote) + if handler(vote) { + break + } + } +} + +// Iterate over votes in the store +func (k Keeper) iterateVotesWithPrefix(ctx sdk.Context, prefix []byte, handler func(vote types.PriceVote) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + var vote types.PriceVote + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vote) + if handler(vote) { + break + } + } +} + +// Retrieves a prevote from the store +func (k Keeper) GetPrevote(ctx sdk.Context, denom string, voter sdk.ValAddress) (prevote types.PricePrevote, err sdk.Error) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetPrevoteKey(denom, voter)) + if b == nil { + err = types.ErrNoPrevote(k.codespace, voter, denom) + return + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &prevote) + return +} + +// Add a prevote to the store +func (k Keeper) AddPrevote(ctx sdk.Context, prevote types.PricePrevote) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(prevote) + store.Set(types.GetPrevoteKey(prevote.Denom, prevote.Voter), bz) +} + +// Delete a prevote from the store +func (k Keeper) DeletePrevote(ctx sdk.Context, prevote types.PricePrevote) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetPrevoteKey(prevote.Denom, prevote.Voter)) +} + +// Retrieves a vote from the store +func (k Keeper) getVote(ctx sdk.Context, denom string, voter sdk.ValAddress) (vote types.PriceVote, err sdk.Error) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetVoteKey(denom, voter)) + if b == nil { + err = types.ErrNoVote(k.codespace, voter, denom) + return + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &vote) + return +} + +// Add a vote to the store +func (k Keeper) AddVote(ctx sdk.Context, vote types.PriceVote) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(vote) + store.Set(types.GetVoteKey(vote.Denom, vote.Voter), bz) +} + +// Delete a vote from the store +func (k Keeper) DeleteVote(ctx sdk.Context, vote types.PriceVote) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetVoteKey(vote.Denom, vote.Voter)) +} + +//----------------------------------- +// Price logic + +// GetLunaPrice gets the consensus exchange rate of Luna denominated in the denom asset from the store. +func (k Keeper) GetLunaPrice(ctx sdk.Context, denom string) (price sdk.Dec, err sdk.Error) { + if denom == core.MicroLunaDenom { + return sdk.OneDec(), nil + } + + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetPriceKey(denom)) + if b == nil { + return sdk.ZeroDec(), types.ErrUnknownDenomination(k.codespace, denom) + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &price) + return +} + +// SetLunaPrice sets the consensus exchange rate of Luna denominated in the denom asset to the store. +func (k Keeper) SetLunaPrice(ctx sdk.Context, denom string, price sdk.Dec) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(price) + store.Set(types.GetPriceKey(denom), bz) +} + +// DeletePrice deletes the consensus exchange rate of Luna denominated in the denom asset from the store. +func (k Keeper) DeletePrice(ctx sdk.Context, denom string) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetPriceKey(denom)) +} + +// Get all active oracle asset denoms from the store +func (k Keeper) GetActiveDenoms(ctx sdk.Context) (denoms types.DenomList) { + denoms = types.DenomList{} + + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.PriceKey) + for ; iter.Valid(); iter.Next() { + n := len(types.PriceKey) + denom := string(iter.Key()[n:]) + denoms = append(denoms, denom) + } + iter.Close() + + return +} + +//----------------------------------- +// Feeder delegation logic + +// GetFeedDelegate gets the account address that the feeder right was delegated to by the validator operator. +func (k Keeper) GetFeedDelegate(ctx sdk.Context, operator sdk.ValAddress) (delegate sdk.AccAddress) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetFeederDelegationKey(operator)) + if b == nil { + // By default the right is delegated to the validator itself + return sdk.AccAddress(operator) + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &delegate) + return +} + +// SetFeedDelegate sets the account address that the feeder right was delegated to by the validator operator. +func (k Keeper) SetFeedDelegate(ctx sdk.Context, operator sdk.ValAddress, delegatedFeeder sdk.AccAddress) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(delegatedFeeder) + store.Set(types.GetFeederDelegationKey(operator), bz) +} + +//----------------------------------- +// Reward pool logic + +// getRewardPool retrieves the reward pool from the store +func (k Keeper) getRewardPool(ctx sdk.Context) sdk.Coins { + acc := k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName) + return acc.GetCoins() +} + +//----------------------------------- +// Claim pool logic + +// Iterate over oracle reward claims in the store +func (k Keeper) IterateClaimPool(ctx sdk.Context, handler func(recipient sdk.ValAddress, weight int64) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.ClaimKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + recipientAddress := iter.Key()[1:] + recipient := sdk.ValAddress(recipientAddress) + + var weight int64 + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &weight) + if handler(recipient, weight) { + break + } + } +} + +// AddClaimPool adds a claim to the the claim pool in the store +func (k Keeper) AddClaimPool(ctx sdk.Context, pool types.ClaimPool) { + store := ctx.KVStore(k.storeKey) + + for _, claim := range pool { + storeKeyClaim := types.GetClaimKey(claim.Recipient) + b := store.Get(storeKeyClaim) + weight := claim.Weight + if b != nil { + var prevWeight int64 + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &prevWeight) + + weight = weight + prevWeight + } + b = k.cdc.MustMarshalBinaryLengthPrefixed(weight) + store.Set(storeKeyClaim, b) + } +} + +// clearClaimPool clears the claim pool from the store +func (k Keeper) clearClaimPool(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + k.IterateClaimPool(ctx, func(recipient sdk.ValAddress, _ int64) (stop bool) { + store.Delete(types.GetClaimKey(recipient)) + return false + }) +} + +//----------------------------------- +// VotingInfo logic + +// SetVotingInfo sets voting info for a validator +func (k Keeper) SetVotingInfo(ctx sdk.Context, address sdk.ValAddress, votingInfo types.VotingInfo) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(votingInfo) + store.Set(types.GetVotingInfoKey(address), bz) + return +} + +// getVotingInfo gets voting info for a validator +func (k Keeper) getVotingInfo(ctx sdk.Context, address sdk.ValAddress) (votingInfo types.VotingInfo, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetVotingInfoKey(address)) + + if bz == nil { + found = false + return + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &votingInfo) + found = true + return +} + +// IterateVotingInfos iterates over the stored VotingInfo +func (k Keeper) IterateVotingInfos(ctx sdk.Context, + handler func(info types.VotingInfo) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.VotingInfoKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + + var info types.VotingInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &info) + + if handler(info) { + break + } + } +} + +//----------------------------------- +// MissedVoteBitArray logic + +// GetMissedVoteBitArray gets the bit for the missed votes array +// only wrong(price) votes will be marked as missed +func (k Keeper) GetMissedVoteBitArray(ctx sdk.Context, address sdk.ValAddress, index int64) (missed bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetMissedVoteBitArrayKey(address, index)) + if bz == nil { + // lazy: treat empty key as not missed + missed = false + return + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) + return +} + +// SetMissedVoteBitArray sets the bit that checks if the validator has +// missed a block in the current window +func (k Keeper) SetMissedVoteBitArray(ctx sdk.Context, address sdk.ValAddress, index int64, missed bool) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed) + store.Set(types.GetMissedVoteBitArrayKey(address, index), bz) +} + +// clearMissedVoteBitArray deletes every instance of MissedVoteBitArray in the store +func (k Keeper) clearMissedVoteBitArray(ctx sdk.Context, address sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.GetMissedVoteBitArrayPrefixKey(address)) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } +} + +// IterateMissedVoteBitArray iterates over the signed votes window +// and performs a callback function +func (k Keeper) IterateMissedVoteBitArray(ctx sdk.Context, + address sdk.ValAddress, handler func(index int64, missed bool) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + index := int64(0) + // Array may be sparse + for ; index < k.VotesWindow(ctx); index++ { + var missed bool + bz := store.Get(types.GetMissedVoteBitArrayKey(address, index)) + if bz == nil { + continue + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) + if handler(index, missed) { + break + } + } +} diff --git a/x/oracle/internal/keeper/keeper_test.go b/x/oracle/internal/keeper/keeper_test.go new file mode 100644 index 000000000..af146d5c4 --- /dev/null +++ b/x/oracle/internal/keeper/keeper_test.go @@ -0,0 +1,321 @@ +package keeper + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestPrevoteAddDelete(t *testing.T) { + input := CreateTestInput(t) + + prevote := types.NewPricePrevote("", core.MicroSDRDenom, sdk.ValAddress(Addrs[0]), 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote) + + KPrevote, err := input.OracleKeeper.GetPrevote(input.Ctx, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + require.NoError(t, err) + require.Equal(t, prevote, KPrevote) + + input.OracleKeeper.DeletePrevote(input.Ctx, prevote) + _, err = input.OracleKeeper.GetPrevote(input.Ctx, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + require.Error(t, err) +} + +func TestPrevoteIterate(t *testing.T) { + input := CreateTestInput(t) + + prevote1 := types.NewPricePrevote("", core.MicroSDRDenom, sdk.ValAddress(Addrs[0]), 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote1) + + prevote2 := types.NewPricePrevote("", core.MicroSDRDenom, sdk.ValAddress(Addrs[1]), 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote2) + + i := 0 + bigger := bytes.Compare(Addrs[0], Addrs[1]) + input.OracleKeeper.IteratePrevotes(input.Ctx, func(p types.PricePrevote) (stop bool) { + if (i == 0 && bigger == -1) || (i == 1 && bigger == 1) { + require.Equal(t, prevote1, p) + } else { + require.Equal(t, prevote2, p) + } + + i++ + return false + }) + + prevote3 := types.NewPricePrevote("", core.MicroLunaDenom, sdk.ValAddress(Addrs[2]), 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote3) + + input.OracleKeeper.iteratePrevotesWithPrefix(input.Ctx, types.GetPrevoteKey(core.MicroLunaDenom, sdk.ValAddress{}), func(p types.PricePrevote) (stop bool) { + require.Equal(t, prevote3, p) + return false + }) +} + +func TestVoteAddDelete(t *testing.T) { + input := CreateTestInput(t) + + price := sdk.NewDec(1700) + vote := types.NewPriceVote(price, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + input.OracleKeeper.AddVote(input.Ctx, vote) + + KVote, err := input.OracleKeeper.getVote(input.Ctx, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + require.NoError(t, err) + require.Equal(t, vote, KVote) + + input.OracleKeeper.DeleteVote(input.Ctx, vote) + _, err = input.OracleKeeper.getVote(input.Ctx, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + require.Error(t, err) +} + +func TestVoteIterate(t *testing.T) { + input := CreateTestInput(t) + + price := sdk.NewDec(1700) + vote1 := types.NewPriceVote(price, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + input.OracleKeeper.AddVote(input.Ctx, vote1) + + vote2 := types.NewPriceVote(price, core.MicroSDRDenom, sdk.ValAddress(Addrs[1])) + input.OracleKeeper.AddVote(input.Ctx, vote2) + + i := 0 + bigger := bytes.Compare(Addrs[0], Addrs[1]) + input.OracleKeeper.IterateVotes(input.Ctx, func(p types.PriceVote) (stop bool) { + if (i == 0 && bigger == -1) || (i == 1 && bigger == 1) { + require.Equal(t, vote1, p) + } else { + require.Equal(t, vote2, p) + } + + i++ + return false + }) + + vote3 := types.NewPriceVote(price, core.MicroLunaDenom, sdk.ValAddress(Addrs[2])) + input.OracleKeeper.AddVote(input.Ctx, vote3) + + input.OracleKeeper.iterateVotesWithPrefix(input.Ctx, types.GetVoteKey(core.MicroLunaDenom, sdk.ValAddress{}), func(p types.PriceVote) (stop bool) { + require.Equal(t, vote3, p) + return false + }) +} + +func TestVoteCollect(t *testing.T) { + input := CreateTestInput(t) + + price := sdk.NewDec(1700) + vote1 := types.NewPriceVote(price, core.MicroSDRDenom, sdk.ValAddress(Addrs[0])) + input.OracleKeeper.AddVote(input.Ctx, vote1) + + vote2 := types.NewPriceVote(price, core.MicroSDRDenom, sdk.ValAddress(Addrs[1])) + input.OracleKeeper.AddVote(input.Ctx, vote2) + + vote3 := types.NewPriceVote(price, core.MicroLunaDenom, sdk.ValAddress(Addrs[0])) + input.OracleKeeper.AddVote(input.Ctx, vote3) + + vote4 := types.NewPriceVote(price, core.MicroLunaDenom, sdk.ValAddress(Addrs[1])) + input.OracleKeeper.AddVote(input.Ctx, vote4) + + collectedVotes := input.OracleKeeper.CollectVotes(input.Ctx) + + pb1 := collectedVotes[core.MicroSDRDenom] + pb2 := collectedVotes[core.MicroLunaDenom] + + bigger := bytes.Compare(Addrs[0], Addrs[1]) + for i, v := range pb1 { + if (i == 0 && bigger == -1) || (i == 1 && bigger == 1) { + require.Equal(t, vote1, v) + } else { + require.Equal(t, vote2, v) + } + } + + for i, v := range pb2 { + if (i == 0 && bigger == -1) || (i == 1 && bigger == 1) { + require.Equal(t, vote3, v) + } else { + require.Equal(t, vote4, v) + } + } +} + +func TestPrice(t *testing.T) { + input := CreateTestInput(t) + + cnyPrice := sdk.NewDecWithPrec(839, int64(OracleDecPrecision)).MulInt64(core.MicroUnit) + gbpPrice := sdk.NewDecWithPrec(4995, int64(OracleDecPrecision)).MulInt64(core.MicroUnit) + krwPrice := sdk.NewDecWithPrec(2838, int64(OracleDecPrecision)).MulInt64(core.MicroUnit) + lunaPrice := sdk.NewDecWithPrec(3282384, int64(OracleDecPrecision)).MulInt64(core.MicroUnit) + + // Set & get prices + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroCNYDenom, cnyPrice) + price, err := input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroCNYDenom) + require.NoError(t, err) + require.Equal(t, cnyPrice, price) + + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroGBPDenom, gbpPrice) + price, err = input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroGBPDenom) + require.NoError(t, err) + require.Equal(t, gbpPrice, price) + + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, krwPrice) + price, err = input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroKRWDenom) + require.NoError(t, err) + require.Equal(t, krwPrice, price) + + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroLunaDenom, lunaPrice) + price, _ = input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroLunaDenom) + require.Equal(t, sdk.OneDec(), price) + + input.OracleKeeper.DeletePrice(input.Ctx, core.MicroKRWDenom) + _, err = input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroKRWDenom) + require.Error(t, err) +} + +func TestRewardPool(t *testing.T) { + input := CreateTestInput(t) + + fees := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000))) + acc := input.SupplyKeeper.GetModuleAccount(input.Ctx, types.ModuleName) + err := acc.SetCoins(fees) + if err != nil { + panic(err) // nerver occurs + } + + input.SupplyKeeper.SetModuleAccount(input.Ctx, acc) + + KFees := input.OracleKeeper.getRewardPool(input.Ctx) + require.Equal(t, fees, KFees) +} + +func TestClaimPool(t *testing.T) { + input := CreateTestInput(t) + + // Test addClaimPool + claim := types.NewClaim(10, ValAddrs[0]) + claim2 := types.NewClaim(20, ValAddrs[1]) + claimPool := types.ClaimPool{claim, claim2} + input.OracleKeeper.AddClaimPool(input.Ctx, claimPool) + + claim = types.NewClaim(15, ValAddrs[0]) + claim2 = types.NewClaim(30, ValAddrs[2]) + claimPool = types.ClaimPool{claim, claim2} + input.OracleKeeper.AddClaimPool(input.Ctx, claimPool) + + // Test IterateClaimPool + input.OracleKeeper.IterateClaimPool(input.Ctx, func(recipient sdk.ValAddress, weight int64) (stop bool) { + if recipient.Equals(ValAddrs[0]) { + require.Equal(t, int64(25), weight) + } else if recipient.Equals(ValAddrs[1]) { + require.Equal(t, int64(20), weight) + } else if recipient.Equals(ValAddrs[2]) { + require.Equal(t, int64(30), weight) + } + return false + }) +} + +func TestParams(t *testing.T) { + input := CreateTestInput(t) + + // Test default params setting + input.OracleKeeper.SetParams(input.Ctx, types.DefaultParams()) + params := input.OracleKeeper.GetParams(input.Ctx) + require.NotNil(t, params) + + // Test custom params setting + votePeriod := int64(10) + voteThreshold := sdk.NewDecWithPrec(1, 10) + oracleRewardBand := sdk.NewDecWithPrec(1, 2) + votesWindow := int64(2000) + minValidVotesPerWindow := sdk.NewDecWithPrec(1, 2) + slashFraction := sdk.NewDecWithPrec(5, 2) + rewardFraction := sdk.NewDecWithPrec(1, 2) + + // Should really test validateParams, but skipping because obvious + newParams := types.Params{ + VotePeriod: votePeriod, + VoteThreshold: voteThreshold, + RewardBand: oracleRewardBand, + VotesWindow: votesWindow, + MinValidVotesPerWindow: minValidVotesPerWindow, + SlashFraction: slashFraction, + RewardFraction: rewardFraction, + } + input.OracleKeeper.SetParams(input.Ctx, newParams) + + storedParams := input.OracleKeeper.GetParams(input.Ctx) + require.NotNil(t, storedParams) + require.Equal(t, newParams, storedParams) +} + +func TestFeederDelegation(t *testing.T) { + input := CreateTestInput(t) + + // Test default getters and setters + delegate := input.OracleKeeper.GetFeedDelegate(input.Ctx, ValAddrs[0]) + require.Equal(t, delegate, Addrs[0]) + + input.OracleKeeper.SetFeedDelegate(input.Ctx, ValAddrs[0], Addrs[1]) + delegate = input.OracleKeeper.GetFeedDelegate(input.Ctx, ValAddrs[0]) + require.Equal(t, delegate, Addrs[1]) +} + +func TestVotingInfo(t *testing.T) { + input := CreateTestInput(t) + + // voting info not found + _, found := input.OracleKeeper.getVotingInfo(input.Ctx, ValAddrs[0]) + require.False(t, found) + + // register voting info + votingInfo := types.NewVotingInfo(ValAddrs[0], 7, 1, 32) + input.OracleKeeper.SetVotingInfo(input.Ctx, ValAddrs[0], votingInfo) + + KVotingInfo, found := input.OracleKeeper.getVotingInfo(input.Ctx, ValAddrs[0]) + require.True(t, found) + require.Equal(t, votingInfo, KVotingInfo) + + votingInfo2 := types.NewVotingInfo(ValAddrs[1], 1, 2, 3) + input.OracleKeeper.SetVotingInfo(input.Ctx, ValAddrs[1], votingInfo2) + + i := 0 + bigger := bytes.Compare(Addrs[0].Bytes(), Addrs[1].Bytes()) + input.OracleKeeper.IterateVotingInfos(input.Ctx, func(info types.VotingInfo) (stop bool) { + if (i == 0 && bigger == -1) || (i == 1 && bigger == 1) { + require.Equal(t, votingInfo, info) + } else { + require.Equal(t, votingInfo2, info) + } + i++ + return false + }) + +} + +func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { + input := CreateTestInput(t) + missed := input.OracleKeeper.GetMissedVoteBitArray(input.Ctx, ValAddrs[0], 0) + require.False(t, missed) // treat empty key as not missed + input.OracleKeeper.SetMissedVoteBitArray(input.Ctx, ValAddrs[0], 0, true) + missed = input.OracleKeeper.GetMissedVoteBitArray(input.Ctx, ValAddrs[0], 0) + require.True(t, missed) // now should be missed + + // iterate 1 bit array + input.OracleKeeper.IterateMissedVoteBitArray(input.Ctx, ValAddrs[0], func(index int64, missed bool) (stop bool) { + require.Equal(t, int64(0), index) + require.True(t, missed) + return false + }) + + // clear vote bit array + input.OracleKeeper.clearMissedVoteBitArray(input.Ctx, ValAddrs[0]) + missed = input.OracleKeeper.GetMissedVoteBitArray(input.Ctx, ValAddrs[0], 0) + require.False(t, missed) // treat empty key as not missed +} diff --git a/x/oracle/internal/keeper/params.go b/x/oracle/internal/keeper/params.go new file mode 100644 index 000000000..19dc24839 --- /dev/null +++ b/x/oracle/internal/keeper/params.go @@ -0,0 +1,69 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// ParamTable for staking module +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&types.Params{}) +} + +// VotePeriod +func (k Keeper) VotePeriod(ctx sdk.Context) (res int64) { + k.paramSpace.Get(ctx, types.ParamStoreKeyVotePeriod, &res) + return +} + +// VoteThreshold +func (k Keeper) VoteThreshold(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyVoteThreshold, &res) + return +} + +// RewardBand +func (k Keeper) RewardBand(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyRewardBand, &res) + return +} + +// RewardFraction +func (k Keeper) RewardFraction(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyRewardFraction, &res) + return +} + +// VotesWindow +func (k Keeper) VotesWindow(ctx sdk.Context) (res int64) { + k.paramSpace.Get(ctx, types.ParamStoreKeyVotesWindow, &res) + return +} + +// MinValidVotesPerWindow +func (k Keeper) MinValidVotesPerWindow(ctx sdk.Context) (res int64) { + var minValidVotesPerWindow sdk.Dec + k.paramSpace.Get(ctx, types.ParamStoreKeyMinValidVotesPerWindow, &minValidVotesPerWindow) + signedBlocksWindow := k.VotesWindow(ctx) + + // NOTE: RoundInt64 will never panic as minValidVotesPerWindow is less than 1. + return minValidVotesPerWindow.MulInt64(signedBlocksWindow).RoundInt64() +} + +// SlashFraction +func (k Keeper) SlashFraction(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeySlashFraction, &res) + return +} + +// GetParams returns the total set of oracle parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of oracle parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/oracle/internal/keeper/querier.go b/x/oracle/internal/keeper/querier.go new file mode 100644 index 000000000..2eeb152b0 --- /dev/null +++ b/x/oracle/internal/keeper/querier.go @@ -0,0 +1,224 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/terra-project/core/x/oracle/internal/types" +) + +// NewQuerier is the module level router for state queries +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case types.QueryPrice: + return queryPrice(ctx, req, keeper) + case types.QueryActives: + return queryActives(ctx, keeper) + case types.QueryVotes: + return queryVotes(ctx, req, keeper) + case types.QueryPrevotes: + return queryPrevotes(ctx, req, keeper) + case types.QueryParameters: + return queryParameters(ctx, keeper) + case types.QueryFeederDelegation: + return queryFeederDelegation(ctx, req, keeper) + case types.QueryVotingInfo: + return queryVotingInfo(ctx, req, keeper) + case types.QueryVotingInfos: + return queryVotingInfos(ctx, req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown oracle query endpoint") + } + } +} + +func queryPrice(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryPriceParams + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(err.Error()) + } + + price, err := keeper.GetLunaPrice(ctx, params.Denom) + if err != nil { + return nil, types.ErrUnknownDenomination(types.DefaultCodespace, params.Denom) + } + + bz, err2 := codec.MarshalJSONIndent(keeper.cdc, price) + if err2 != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + } + + return bz, nil +} + +func queryActives(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { + denoms := keeper.GetActiveDenoms(ctx) + + bz, err := codec.MarshalJSONIndent(keeper.cdc, denoms) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} + +func queryVotes(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryVotesParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + filteredVotes := types.PriceVotes{} + + // collects all votes without filter + prefix := types.VoteKey + handler := func(vote types.PriceVote) (stop bool) { + filteredVotes = append(filteredVotes, vote) + return false + } + + // applies filter + if len(params.Denom) != 0 && !params.Voter.Empty() { + prefix = types.GetVoteKey(params.Denom, params.Voter) + } else if len(params.Denom) != 0 { + prefix = types.GetVoteKey(params.Denom, sdk.ValAddress{}) + } else if !params.Voter.Empty() { + handler = func(vote types.PriceVote) (stop bool) { + + if vote.Voter.Equals(params.Voter) { + filteredVotes = append(filteredVotes, vote) + } + + return false + } + } + + keeper.iterateVotesWithPrefix(ctx, prefix, handler) + + bz, err := codec.MarshalJSONIndent(keeper.cdc, filteredVotes) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryPrevotes(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryPrevotesParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + filteredPrevotes := types.PricePrevotes{} + + // collects all votes without filter + prefix := types.PrevoteKey + handler := func(prevote types.PricePrevote) (stop bool) { + filteredPrevotes = append(filteredPrevotes, prevote) + return false + } + + // applies filter + if len(params.Denom) != 0 && !params.Voter.Empty() { + prefix = types.GetPrevoteKey(params.Denom, params.Voter) + } else if len(params.Denom) != 0 { + prefix = types.GetPrevoteKey(params.Denom, sdk.ValAddress{}) + } else if !params.Voter.Empty() { + handler = func(prevote types.PricePrevote) (stop bool) { + + if prevote.Voter.Equals(params.Voter) { + filteredPrevotes = append(filteredPrevotes, prevote) + } + + return false + } + } + + keeper.iteratePrevotesWithPrefix(ctx, prefix, handler) + + bz, err := codec.MarshalJSONIndent(keeper.cdc, filteredPrevotes) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryParameters(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryFeederDelegation(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryFeederDelegationParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + delegatee := keeper.GetFeedDelegate(ctx, params.Validator) + bz, err := codec.MarshalJSONIndent(keeper.cdc, delegatee) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryVotingInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryVotingInfoParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to parse params", err.Error())) + } + + signingInfo, found := k.getVotingInfo(ctx, params.ValAddress) + if !found { + return nil, types.ErrNoVotingInfoFound(types.DefaultCodespace, params.ValAddress) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfo) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error())) + } + + return res, nil +} + +func queryVotingInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryVotingInfosParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to parse params", err.Error())) + } + + var votingInfos []types.VotingInfo + + k.IterateVotingInfos(ctx, func(info types.VotingInfo) (stop bool) { + votingInfos = append(votingInfos, info) + return false + }) + + start, end := client.Paginate(len(votingInfos), params.Page, params.Limit, int(k.StakingKeeper.MaxValidators(ctx))) + if start < 0 || end < 0 { + votingInfos = []types.VotingInfo{} + } else { + votingInfos = votingInfos[start:end] + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, votingInfos) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error())) + } + + return res, nil +} diff --git a/x/oracle/internal/keeper/querier_test.go b/x/oracle/internal/keeper/querier_test.go new file mode 100644 index 000000000..79486b52c --- /dev/null +++ b/x/oracle/internal/keeper/querier_test.go @@ -0,0 +1,319 @@ +package keeper + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +func TestNewQuerier(t *testing.T) { + input := CreateTestInput(t) + + querier := NewQuerier(input.OracleKeeper) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + _, err := querier(input.Ctx, []string{types.QueryParameters}, query) + require.NoError(t, err) +} + +func TestQueryParams(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + + var params types.Params + + res, errRes := queryParameters(input.Ctx, input.OracleKeeper) + require.NoError(t, errRes) + + err := cdc.UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + require.Equal(t, input.OracleKeeper.GetParams(input.Ctx), params) +} + +func TestQueryPrevotes(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + prevote1 := types.NewPricePrevote("", core.MicroSDRDenom, ValAddrs[0], 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote1) + prevote2 := types.NewPricePrevote("", core.MicroSDRDenom, ValAddrs[1], 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote2) + prevote3 := types.NewPricePrevote("", core.MicroLunaDenom, ValAddrs[2], 0) + input.OracleKeeper.AddPrevote(input.Ctx, prevote3) + + // voter denom both query params + queryParams := types.NewQueryPrevotesParams(ValAddrs[0], core.MicroSDRDenom) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryPrevotes}, req) + require.NoError(t, err) + + var filteredPrevotes types.PricePrevotes + err = cdc.UnmarshalJSON(res, &filteredPrevotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredPrevotes)) + require.Equal(t, prevote1, filteredPrevotes[0]) + + // voter query params + queryParams = types.NewQueryPrevotesParams(ValAddrs[0], "") + bz, err = cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req = abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err = querier(input.Ctx, []string{types.QueryPrevotes}, req) + require.NoError(t, err) + + err = cdc.UnmarshalJSON(res, &filteredPrevotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredPrevotes)) + require.Equal(t, prevote1, filteredPrevotes[0]) + + // denom query params + queryParams = types.NewQueryPrevotesParams(sdk.ValAddress{}, core.MicroLunaDenom) + bz, err = cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req = abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err = querier(input.Ctx, []string{types.QueryPrevotes}, req) + require.NoError(t, err) + + err = cdc.UnmarshalJSON(res, &filteredPrevotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredPrevotes)) + require.Equal(t, prevote3, filteredPrevotes[0]) +} + +func TestQueryVotes(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + vote1 := types.NewPriceVote(sdk.NewDec(1700), core.MicroSDRDenom, ValAddrs[0]) + input.OracleKeeper.AddVote(input.Ctx, vote1) + vote2 := types.NewPriceVote(sdk.NewDec(1700), core.MicroSDRDenom, ValAddrs[1]) + input.OracleKeeper.AddVote(input.Ctx, vote2) + vote3 := types.NewPriceVote(sdk.NewDec(1700), core.MicroLunaDenom, ValAddrs[2]) + input.OracleKeeper.AddVote(input.Ctx, vote3) + + // voter denom both query params + queryParams := types.NewQueryVotesParams(ValAddrs[0], core.MicroSDRDenom) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryVotes}, req) + require.NoError(t, err) + + var filteredVotes types.PriceVotes + err = cdc.UnmarshalJSON(res, &filteredVotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredVotes)) + require.Equal(t, vote1, filteredVotes[0]) + + // voter query params + queryParams = types.NewQueryVotesParams(ValAddrs[0], "") + bz, err = cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req = abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err = querier(input.Ctx, []string{types.QueryVotes}, req) + require.NoError(t, err) + + err = cdc.UnmarshalJSON(res, &filteredVotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredVotes)) + require.Equal(t, vote1, filteredVotes[0]) + + // denom query params + queryParams = types.NewQueryVotesParams(sdk.ValAddress{}, core.MicroLunaDenom) + bz, err = cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req = abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err = querier(input.Ctx, []string{types.QueryVotes}, req) + require.NoError(t, err) + + err = cdc.UnmarshalJSON(res, &filteredVotes) + require.NoError(t, err) + require.Equal(t, 1, len(filteredVotes)) + require.Equal(t, vote3, filteredVotes[0]) +} + +func TestQueryPrice(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + price := sdk.NewDec(1700) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, price) + + // denom query params + queryParams := types.NewQueryPriceParams(core.MicroSDRDenom) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryPrice}, req) + require.NoError(t, err) + + var rprice sdk.Dec + err = cdc.UnmarshalJSON(res, &rprice) + require.NoError(t, err) + require.Equal(t, price, rprice) +} + +func TestQueryActives(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + price := sdk.NewDec(1700) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, price) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, price) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroUSDDenom, price) + + res, err := querier(input.Ctx, []string{types.QueryActives}, abci.RequestQuery{}) + require.NoError(t, err) + + targetDenoms := types.DenomList{ + core.MicroKRWDenom, + core.MicroSDRDenom, + core.MicroUSDDenom, + } + var denoms types.DenomList + err2 := cdc.UnmarshalJSON(res, &denoms) + require.NoError(t, err2) + require.Equal(t, targetDenoms, denoms) +} + +func TestQueryFeederDelegation(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + input.OracleKeeper.SetFeedDelegate(input.Ctx, ValAddrs[0], Addrs[1]) + + queryParams := types.NewQueryFeederDelegationParams(ValAddrs[0]) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryFeederDelegation}, req) + require.NoError(t, err) + + var delegatee sdk.AccAddress + cdc.UnmarshalJSON(res, &delegatee) + require.Equal(t, Addrs[1], delegatee) +} + +func TestQueryVotingInfo(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + votingInfo := types.NewVotingInfo(ValAddrs[0], 7, 1, 32) + input.OracleKeeper.SetVotingInfo(input.Ctx, ValAddrs[0], votingInfo) + + queryParams := types.NewQueryVotingInfoParams(ValAddrs[0]) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryVotingInfo}, req) + require.NoError(t, err) + + var resVotingInfo types.VotingInfo + cdc.UnmarshalJSON(res, &resVotingInfo) + require.Equal(t, votingInfo, resVotingInfo) +} + +func TestQueryVotingInfos(t *testing.T) { + cdc := codec.New() + input := CreateTestInput(t) + querier := NewQuerier(input.OracleKeeper) + + votingInfo1 := types.NewVotingInfo(ValAddrs[0], 7, 1, 32) + input.OracleKeeper.SetVotingInfo(input.Ctx, ValAddrs[0], votingInfo1) + votingInfo2 := types.NewVotingInfo(ValAddrs[1], 7, 1, 32) + input.OracleKeeper.SetVotingInfo(input.Ctx, ValAddrs[1], votingInfo2) + + queryParams := types.NewQueryVotingInfosParams(1, 2) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) + + req := abci.RequestQuery{ + Path: "", + Data: bz, + } + + res, err := querier(input.Ctx, []string{types.QueryVotingInfos}, req) + require.NoError(t, err) + + var resVotingInfos []types.VotingInfo + cdc.UnmarshalJSON(res, &resVotingInfos) + + bigger := bytes.Compare(Addrs[0].Bytes(), Addrs[1].Bytes()) + var votingInfos []types.VotingInfo + if bigger == 1 { + votingInfos = []types.VotingInfo{ + votingInfo2, votingInfo1, + } + } else { + votingInfos = []types.VotingInfo{ + votingInfo1, votingInfo2, + } + } + + require.Equal(t, votingInfos, resVotingInfos) +} diff --git a/x/oracle/internal/keeper/reward.go b/x/oracle/internal/keeper/reward.go new file mode 100644 index 000000000..bfeb097a1 --- /dev/null +++ b/x/oracle/internal/keeper/reward.go @@ -0,0 +1,57 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/terra-project/core/x/oracle/internal/types" +) + +// At the end of every VotePeriod, we give out portion of seigniorage reward(reward-weight) to the +// oracle voters that voted faithfully. +func (k Keeper) RewardPrevBallotWinners(ctx sdk.Context) { + // Sum weight of the claimpool + prevBallotWeightSum := int64(0) + k.IterateClaimPool(ctx, func(_ sdk.ValAddress, weight int64) (stop bool) { + prevBallotWeightSum += weight + return false + }) + + if prevBallotWeightSum != 0 { + rewardPool := k.getRewardPool(ctx) + if !rewardPool.Empty() { + // if rewardFraction = 1%; 1/100 module balance will be distributed + rewardFraction := k.RewardFraction(ctx) + + // Dole out rewards + var distributedReward sdk.Coins + k.IterateClaimPool(ctx, func(recipient sdk.ValAddress, weight int64) (stop bool) { + + rewardCoins := sdk.NewCoins() + rewardeeVal := k.StakingKeeper.Validator(ctx, recipient) + for _, feeCoin := range rewardPool { + rewardAmt := sdk.NewDecFromInt(feeCoin.Amount).Mul(rewardFraction).QuoInt64(prevBallotWeightSum).MulInt64(weight).TruncateInt() + rewardCoins = append(rewardCoins, sdk.NewCoin(feeCoin.Denom, rewardAmt)) + } + + // In case absence of the validator, we just skip distribution + if rewardeeVal != nil { + k.distrKeeper.AllocateTokensToValidator(ctx, rewardeeVal, sdk.NewDecCoins(rewardCoins)) + distributedReward = distributedReward.Add(rewardCoins) + } + + return false + }) + + // Move distributed reward to distribution module + err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.distrName, distributedReward) + if err != nil { + panic(fmt.Sprintf("[oracle] Failed to send coins to distribution module %s", err.Error())) + } + } + + // Clear claim and fee pool + k.clearClaimPool(ctx) + } +} diff --git a/x/oracle/internal/keeper/reward_test.go b/x/oracle/internal/keeper/reward_test.go new file mode 100644 index 000000000..90ae86498 --- /dev/null +++ b/x/oracle/internal/keeper/reward_test.go @@ -0,0 +1,64 @@ +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// Test a reward giving mechanism +func TestRewardPrevBallotWinners(t *testing.T) { + // initial setup + input := CreateTestInput(t) + addr, val := ValAddrs[0], PubKeys[0] + addr1, val1 := ValAddrs[1], PubKeys[1] + amt := sdk.TokensFromConsensusPower(100) + sh := staking.NewHandler(input.StakingKeeper) + ctx := input.Ctx + + // Validator created + got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, got.IsOK()) + got = sh(ctx, NewTestMsgCreateValidator(addr1, val1, amt)) + require.True(t, got.IsOK()) + staking.EndBlocker(ctx, input.StakingKeeper) + + require.Equal( + t, input.BankKeeper.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(input.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + ) + require.Equal(t, amt, input.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) + require.Equal( + t, input.BankKeeper.GetCoins(ctx, sdk.AccAddress(addr1)), + sdk.NewCoins(sdk.NewCoin(input.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + ) + require.Equal(t, amt, input.StakingKeeper.Validator(ctx, addr1).GetBondedTokens()) + + // Add claim pools + claim := types.NewClaim(10, addr) + claim2 := types.NewClaim(20, addr1) + claimPool := types.ClaimPool{claim, claim2} + input.OracleKeeper.AddClaimPool(input.Ctx, claimPool) + + // Prepare reward pool + givingAmt := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 3000)) + acc := input.SupplyKeeper.GetModuleAccount(ctx, types.ModuleName) + err := acc.SetCoins(givingAmt) + require.NoError(t, err) + input.SupplyKeeper.SetModuleAccount(ctx, acc) + + input.OracleKeeper.RewardPrevBallotWinners(ctx) + outstandingRewards := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr) + require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).Mul(input.OracleKeeper.RewardFraction(ctx)).QuoInt64(3), + outstandingRewards.AmountOf(core.MicroLunaDenom)) + + outstandingRewards1 := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr1) + require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).Mul(input.OracleKeeper.RewardFraction(ctx)).QuoInt64(3).MulInt64(2), + outstandingRewards1.AmountOf(core.MicroLunaDenom)) +} diff --git a/x/oracle/internal/keeper/test_utils.go b/x/oracle/internal/keeper/test_utils.go new file mode 100644 index 000000000..fdb08be57 --- /dev/null +++ b/x/oracle/internal/keeper/test_utils.go @@ -0,0 +1,195 @@ +// nolint:deadcode unused noalias +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/types" + + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +var ( + PubKeys = []crypto.PubKey{ + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + } + + Addrs = []sdk.AccAddress{ + sdk.AccAddress(PubKeys[0].Address()), + sdk.AccAddress(PubKeys[1].Address()), + sdk.AccAddress(PubKeys[2].Address()), + } + + ValAddrs = []sdk.ValAddress{ + sdk.ValAddress(PubKeys[0].Address()), + sdk.ValAddress(PubKeys[1].Address()), + sdk.ValAddress(PubKeys[2].Address()), + } + + InitTokens = sdk.TokensFromConsensusPower(200) + InitCoins = sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens)) + + OracleDecPrecision = 8 +) + +// TestInput nolint +type TestInput struct { + Ctx sdk.Context + Cdc *codec.Codec + AccKeeper auth.AccountKeeper + BankKeeper bank.Keeper + OracleKeeper Keeper + SupplyKeeper supply.Keeper + StakingKeeper staking.Keeper + DistrKeeper distr.Keeper +} + +func newTestCodec() *codec.Codec { + cdc := codec.New() + + types.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + supply.RegisterCodec(cdc) + staking.RegisterCodec(cdc) + distr.RegisterCodec(cdc) + params.RegisterCodec(cdc) + + return cdc +} + +// CreateTestInput nolint +func CreateTestInput(t *testing.T) TestInput { + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keyOracle := sdk.NewKVStoreKey(types.StoreKey) + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) + keyDistr := sdk.NewKVStoreKey(distr.StoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + + cdc := newTestCodec() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) + + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + + require.NoError(t, ms.LoadLatestVersion()) + + blackListAddrs := map[string]bool{ + auth.FeeCollectorName: true, + staking.NotBondedPoolName: true, + staking.BondedPoolName: true, + distr.ModuleName: true, + types.ModuleName: true, + } + + paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams, params.DefaultCodespace) + accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) + bankKeeper := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blackListAddrs) + + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + distr.ModuleName: nil, + types.ModuleName: nil, + } + + supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) + totalSupply := sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens.MulRaw(int64(len(Addrs))))) + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + stakingKeeper := staking.NewKeeper( + cdc, + keyStaking, tKeyStaking, + supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), + staking.DefaultCodespace, + ) + + distrKeeper := distr.NewKeeper( + cdc, + keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), + stakingKeeper, supplyKeeper, + distr.DefaultCodespace, auth.FeeCollectorName, blackListAddrs) + + distrKeeper.SetFeePool(ctx, distr.InitialFeePool()) + distrKeeper.SetCommunityTax(ctx, sdk.NewDecWithPrec(2, 2)) + distrKeeper.SetBaseProposerReward(ctx, sdk.NewDecWithPrec(1, 2)) + distrKeeper.SetBonusProposerReward(ctx, sdk.NewDecWithPrec(4, 2)) + + feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) + distrAcc := supply.NewEmptyModuleAccount(distr.ModuleName) + oracleAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Minter) + + notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens.MulRaw(int64(len(Addrs)))))) + + supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) + supplyKeeper.SetModuleAccount(ctx, bondPool) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + supplyKeeper.SetModuleAccount(ctx, distrAcc) + supplyKeeper.SetModuleAccount(ctx, oracleAcc) + + genesis := staking.DefaultGenesisState() + genesis.Params.BondDenom = core.MicroLunaDenom + _ = staking.InitGenesis(ctx, stakingKeeper, accountKeeper, supplyKeeper, genesis) + + for _, addr := range Addrs { + _, err := bankKeeper.AddCoins(ctx, sdk.AccAddress(addr), InitCoins) + require.NoError(t, err) + } + + keeper := NewKeeper(cdc, keyOracle, paramsKeeper.Subspace(types.DefaultParamspace), distrKeeper, stakingKeeper, supplyKeeper, distr.ModuleName, types.DefaultCodespace) + + defaults := types.DefaultParams() + keeper.SetParams(ctx, defaults) + + stakingKeeper.SetHooks(staking.NewMultiStakingHooks(distrKeeper.Hooks(), keeper.Hooks())) + + return TestInput{ctx, cdc, accountKeeper, bankKeeper, keeper, supplyKeeper, stakingKeeper, distrKeeper} +} + +func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator { + commission := staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + return staking.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(core.MicroLunaDenom, amt), + staking.Description{}, commission, sdk.OneInt(), + ) +} + +func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) staking.MsgDelegate { + amount := sdk.NewCoin(core.MicroLunaDenom, delAmount) + return staking.NewMsgDelegate(delAddr, valAddr, amount) +} diff --git a/x/oracle/ballot.go b/x/oracle/internal/types/ballot.go similarity index 52% rename from x/oracle/ballot.go rename to x/oracle/internal/types/ballot.go index b10ffc793..8ee776c62 100644 --- a/x/oracle/ballot.go +++ b/x/oracle/internal/types/ballot.go @@ -1,8 +1,10 @@ -package oracle +package types import ( "fmt" + "math" "sort" + "strconv" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -11,34 +13,29 @@ import ( type PriceBallot []PriceVote // Returns the total amount of voting power in the ballot -func (pb PriceBallot) power(ctx sdk.Context, valset sdk.ValidatorSet) sdk.Int { - totalPower := sdk.ZeroInt() +func (pb PriceBallot) Power(ctx sdk.Context, sk StakingKeeper) int64 { + totalPower := int64(0) for _, vote := range pb { - votePower, err := vote.getPower(ctx, valset) - if err == nil { - totalPower = totalPower.Add(votePower) - } + totalPower += vote.getPower(ctx, sk) } + return totalPower } // Returns the median weighted by the power of the PriceVote. -func (pb PriceBallot) weightedMedian(ctx sdk.Context, valset sdk.ValidatorSet) sdk.Dec { - totalPower := pb.power(ctx, valset) +func (pb PriceBallot) WeightedMedian(ctx sdk.Context, sk StakingKeeper) sdk.Dec { + totalPower := pb.Power(ctx, sk) if pb.Len() > 0 { if !sort.IsSorted(pb) { sort.Sort(pb) } - pivot := sdk.ZeroInt() + pivot := int64(0) for _, v := range pb { - votePower, err := v.getPower(ctx, valset) - if err != nil { - continue - } + votePower := v.getPower(ctx, sk) - pivot = pivot.Add(votePower) - if pivot.GTE(totalPower.QuoRaw(2)) { + pivot += votePower + if pivot >= (totalPower / 2) { return v.Price } } @@ -46,6 +43,29 @@ func (pb PriceBallot) weightedMedian(ctx sdk.Context, valset sdk.ValidatorSet) s return sdk.ZeroDec() } +// Returns the standard deviation by the power of the PriceVote. +func (pb PriceBallot) StandardDeviation(ctx sdk.Context, sk StakingKeeper) (standardDeviation sdk.Dec) { + if len(pb) == 0 { + return sdk.ZeroDec() + } + + median := pb.WeightedMedian(ctx, sk) + + sum := sdk.ZeroDec() + for _, v := range pb { + deviation := v.Price.Sub(median) + sum = sum.Add(deviation.Mul(deviation)) + } + + variance := sum.Quo(sdk.NewDec(int64(len(pb)))) + + floatNum, _ := strconv.ParseFloat(variance.String(), 64) + floatNum = math.Sqrt(floatNum) + standardDeviation, _ = sdk.NewDecFromStr(fmt.Sprintf("%f", floatNum)) + + return +} + // Len implements sort.Interface func (pb PriceBallot) Len() int { return len(pb) diff --git a/x/oracle/internal/types/ballot_test.go b/x/oracle/internal/types/ballot_test.go new file mode 100644 index 000000000..7bd070284 --- /dev/null +++ b/x/oracle/internal/types/ballot_test.go @@ -0,0 +1,244 @@ +package types + +import ( + "fmt" + "math" + "strconv" + + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestSqrt(t *testing.T) { + num := sdk.NewDecWithPrec(144, 4) + floatNum, err := strconv.ParseFloat(num.String(), 64) + require.NoError(t, err) + + floatNum = math.Sqrt(floatNum) + num, err = sdk.NewDecFromStr(fmt.Sprintf("%f", floatNum)) + require.NoError(t, err) + + require.Equal(t, sdk.NewDecWithPrec(12, 2), num) +} + +func checkFloatEquality(a sdk.Dec, b float64, precision int) bool { + base := math.Pow10(precision) + + a2 := a.MulInt64(int64(base)).TruncateInt64() + b2 := int64(b * base) + + return a2 == b2 +} + +func TestPBPower(t *testing.T) { + + ctx := sdk.NewContext(nil, abci.Header{}, false, nil) + _, valAccAddrs, sk := GenerateRandomTestCase() + pb := PriceBallot{} + ballotPower := int64(0) + + for i := 0; i < len(sk.Validators()); i++ { + vote := NewPriceVote(sdk.ZeroDec(), core.MicroSDRDenom, sdk.ValAddress(valAccAddrs[i])) + pb = append(pb, vote) + + valPower := vote.getPower(ctx, sk) + require.NotEqual(t, int64(0), valPower) + + ballotPower += valPower + } + + require.Equal(t, ballotPower, pb.Power(ctx, sk)) + + // Mix in a fake validator, the total power should not have changed. + pubKey := secp256k1.GenPrivKey().PubKey() + faceValAddr := sdk.ValAddress(pubKey.Address()) + fakeVote := NewPriceVote(sdk.OneDec(), core.MicroSDRDenom, faceValAddr) + pb = append(pb, fakeVote) + require.Equal(t, ballotPower, pb.Power(ctx, sk)) +} + +func TestPBWeightedMedian(t *testing.T) { + tests := []struct { + inputs []float64 + weights []int64 + isValidator []bool + median sdk.Dec + }{ + { + // Supermajority one number + []float64{1.0, 2.0, 10.0, 100000.0}, + []int64{1, 1, 100, 1}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(10, 0), + }, + { + // Adding fake validator doesn't change outcome + []float64{1.0, 2.0, 10.0, 100000.0, 10000000000}, + []int64{1, 1, 100, 1, 10000}, + []bool{true, true, true, true, false}, + sdk.NewDecWithPrec(10, 0), + }, + { + // Tie votes + []float64{1.0, 2.0, 3.0, 4.0}, + []int64{1, 100, 100, 1}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(2, 0), + }, + { + // No votes + []float64{}, + []int64{}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(0, 0), + }, + } + + var mockValset []MockValidator + base := math.Pow10(oracleDecPrecision) + for _, tc := range tests { + pb := PriceBallot{} + for i, input := range tc.inputs { + valAddr := sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()) + + power := tc.weights[i] + mockVal := NewMockValidator(valAddr, power) + + if tc.isValidator[i] { + mockValset = append(mockValset, mockVal) + } + vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*base), int64(oracleDecPrecision)), core.MicroSDRDenom, valAddr) + pb = append(pb, vote) + } + + sk := NewDummyStakingKeeper(mockValset) + + ctx := sdk.NewContext(nil, abci.Header{}, false, nil) + require.Equal(t, tc.median, pb.WeightedMedian(ctx, sk)) + } +} + +func TestPBStandardDeviation(t *testing.T) { + tests := []struct { + inputs []float64 + weights []int64 + isValidator []bool + standardDeviation sdk.Dec + }{ + { + // Supermajority one number + []float64{1.0, 2.0, 10.0, 100000.0}, + []int64{1, 1, 100, 1}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(49995000363, oracleDecPrecision), + }, + { + // Adding fake validator doesn't change outcome + []float64{1.0, 2.0, 10.0, 100000.0, 10000000000}, + []int64{1, 1, 100, 1, 10000}, + []bool{true, true, true, true, false}, + sdk.NewDecWithPrec(4472135950751006, oracleDecPrecision), + }, + { + // Tie votes + []float64{1.0, 2.0, 3.0, 4.0}, + []int64{1, 100, 100, 1}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(1224745, oracleDecPrecision), + }, + { + // No votes + []float64{}, + []int64{}, + []bool{true, true, true, true}, + sdk.NewDecWithPrec(0, 0), + }, + } + + var mockValset []MockValidator + base := math.Pow10(oracleDecPrecision) + for _, tc := range tests { + pb := PriceBallot{} + for i, input := range tc.inputs { + valAddr := sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()) + + power := tc.weights[i] + mockVal := NewMockValidator(valAddr, power) + + if tc.isValidator[i] { + mockValset = append(mockValset, mockVal) + } + vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*base), int64(oracleDecPrecision)), core.MicroSDRDenom, valAddr) + pb = append(pb, vote) + } + + sk := NewDummyStakingKeeper(mockValset) + + ctx := sdk.NewContext(nil, abci.Header{}, false, nil) + require.Equal(t, tc.standardDeviation, pb.StandardDeviation(ctx, sk)) + } +} + +func TestString(t *testing.T) { + pb := PriceBallot{} + require.Equal(t, "PriceBallot of 0 votes\n", pb.String()) + + vote := NewPriceVote(sdk.NewDecWithPrec(int64(1123400), int64(oracleDecPrecision)), core.MicroSDRDenom, sdk.ValAddress{}) + pb = append(pb, vote) + require.Equal(t, "PriceBallot of 1 votes\n\n PriceVote\n\tDenom: usdr, \n\tVoter: , \n\tPrice: 1.123400000000000000", pb.String()) +} + +// func TestPBTally(t *testing.T) { +// _, addrs :=mock.GeneratePrivKeyAddressPairs(3) +// tests := []struct { +// inputs []float64 +// weights []int64 +// rewardees []sdk.AccAddress +// }{ +// { +// // Supermajority one number +// []float64{1.0, 2.0, 10.0, 100000.0}, +// []int64{1, 1, 100, 1}, +// []sdk.AccAddress{addrs[2]}, +// }, +// { +// // Tie votes +// []float64{1.0, 2.0, 3.0, 4.0}, +// []int64{1, 100, 100, 1}, +// []sdk.AccAddress{addrs[1]}, +// }, +// { +// // No votes +// []float64{}, +// []int64{}, +// []sdk.AccAddress{}, +// }, + +// { +// // Lots of random votes +// []float64{1.0, 78.48, 78.11, 79.0}, +// []int64{1, 51, 79, 33}, +// []sdk.AccAddress{addrs[1], addrs[2], addrs[3]}, +// }, +// } + +// for _, tc := range tests { +// pb := PriceBallot{} +// for i, input := range tc.inputs { +// vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*100), 2), "", +// sdk.NewInt(tc.weights[i]), addrs[i]) +// pb = append(pb, vote) +// } + +// _, rewardees := pb.Tally() +// require.Equal(t, len(tc.rewardees), len(rewardees)) +// } +// } diff --git a/types/claim.go b/x/oracle/internal/types/claim.go similarity index 77% rename from types/claim.go rename to x/oracle/internal/types/claim.go index dbbf49d31..998e42515 100644 --- a/types/claim.go +++ b/x/oracle/internal/types/claim.go @@ -12,12 +12,12 @@ import ( // Claim is an interface that directs its rewards to an attached bank account. type Claim struct { - Weight sdk.Int `json:"weight"` - Recipient sdk.AccAddress `json:"recipient"` + Weight int64 `json:"weight"` + Recipient sdk.ValAddress `json:"recipient"` } // NewClaim generates a Claim instance. -func NewClaim(weight sdk.Int, recipient sdk.AccAddress) Claim { +func NewClaim(weight int64, recipient sdk.ValAddress) Claim { return Claim{ Weight: weight, Recipient: recipient, diff --git a/types/claimpool.go b/x/oracle/internal/types/claimpool.go similarity index 89% rename from types/claimpool.go rename to x/oracle/internal/types/claimpool.go index 1bbf25127..4e7ecc100 100644 --- a/types/claimpool.go +++ b/x/oracle/internal/types/claimpool.go @@ -14,17 +14,18 @@ func (cp ClaimPool) Sort() ClaimPool { for _, claim := range cp { addrStr := claim.Recipient.String() if val, ok := sortBuf[addrStr]; ok { - val.Weight = val.Weight.Add(claim.Weight) + val.Weight = val.Weight + claim.Weight sortBuf[addrStr] = val } else { sortBuf[addrStr] = claim } } + i := 0 cp = make([]Claim, len(sortBuf)) - for _, claim := range sortBuf { - cp = append(cp, claim) + cp[i] = claim + i++ } return cp diff --git a/x/oracle/internal/types/claimpool_test.go b/x/oracle/internal/types/claimpool_test.go new file mode 100644 index 000000000..806505790 --- /dev/null +++ b/x/oracle/internal/types/claimpool_test.go @@ -0,0 +1,23 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/stretchr/testify/require" +) + +func TestClaimPoolSort(t *testing.T) { + _, addrs, _, _ := mock.CreateGenAccounts(2, sdk.Coins{}) + + claim1 := NewClaim(1, sdk.ValAddress(addrs[0])) + claim2 := NewClaim(2, sdk.ValAddress(addrs[0])) + claim3 := NewClaim(3, sdk.ValAddress(addrs[1])) + + claimPool := ClaimPool{claim1, claim2, claim3} + claimPool = claimPool.Sort() + + require.Equal(t, 2, len(claimPool)) + require.Equal(t, int64(3), claimPool[0].Weight) +} diff --git a/x/oracle/codec.go b/x/oracle/internal/types/codec.go similarity index 60% rename from x/oracle/codec.go rename to x/oracle/internal/types/codec.go index f9366bad2..c688d9341 100644 --- a/x/oracle/codec.go +++ b/x/oracle/internal/types/codec.go @@ -1,22 +1,19 @@ -package oracle +package types import ( "github.com/cosmos/cosmos-sdk/codec" ) -var msgCdc = codec.New() +// module codec +var ModuleCdc = codec.New() // RegisterCodec registers concrete types on codec codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgPriceVote{}, "oracle/MsgPriceVote", nil) cdc.RegisterConcrete(MsgPricePrevote{}, "oracle/MsgPricePrevote", nil) cdc.RegisterConcrete(MsgDelegateFeederPermission{}, "oracle/MsgDelegateFeederPermission", nil) - - cdc.RegisterConcrete(&PriceBallot{}, "oracle/PriceBallot", nil) - cdc.RegisterConcrete(&PriceVote{}, "oracle/PriceVote", nil) - cdc.RegisterConcrete(&PricePrevote{}, "oracle/PricePrevote", nil) } func init() { - RegisterCodec(msgCdc) + RegisterCodec(ModuleCdc) } diff --git a/x/oracle/denom.go b/x/oracle/internal/types/denom.go similarity index 91% rename from x/oracle/denom.go rename to x/oracle/internal/types/denom.go index 9e82896cc..a41cb1054 100644 --- a/x/oracle/denom.go +++ b/x/oracle/internal/types/denom.go @@ -1,4 +1,4 @@ -package oracle +package types import ( "strings" diff --git a/x/oracle/errors.go b/x/oracle/internal/types/errors.go similarity index 81% rename from x/oracle/errors.go rename to x/oracle/internal/types/errors.go index 7480bca3d..17dcce338 100644 --- a/x/oracle/errors.go +++ b/x/oracle/internal/types/errors.go @@ -1,4 +1,4 @@ -package oracle +package types import ( "fmt" @@ -7,21 +7,24 @@ import ( "github.com/tendermint/tendermint/crypto/tmhash" ) +type codeType = sdk.CodeType + // Oracle error codes const ( DefaultCodespace sdk.CodespaceType = "oracle" - CodeUnknownDenom sdk.CodeType = 1 - CodeInvalidPrice sdk.CodeType = 2 - CodeVoterNotValidator sdk.CodeType = 3 - CodeInvalidVote sdk.CodeType = 4 - CodeNoVotingPermission sdk.CodeType = 5 - CodeInvalidHashLength sdk.CodeType = 6 - CodeInvalidPrevote sdk.CodeType = 7 - CodeVerificationFailed sdk.CodeType = 8 - CodeNotRevealPeriod sdk.CodeType = 9 - CodeInvalidSaltLength sdk.CodeType = 10 - CodeInvalidMsgFormat sdk.CodeType = 11 + CodeUnknownDenom codeType = 1 + CodeInvalidPrice codeType = 2 + CodeVoterNotValidator codeType = 3 + CodeInvalidVote codeType = 4 + CodeNoVotingPermission codeType = 5 + CodeInvalidHashLength codeType = 6 + CodeInvalidPrevote codeType = 7 + CodeVerificationFailed codeType = 8 + CodeNotRevealPeriod codeType = 9 + CodeInvalidSaltLength codeType = 10 + CodeInvalidMsgFormat codeType = 11 + CodeMissingVotingInfo codeType = 12 ) // ---------------------------------------- @@ -81,3 +84,8 @@ func ErrInvalidSaltLength(codespace sdk.CodespaceType, saltLength int) sdk.Error func ErrInvalidMsgFormat(codespace sdk.CodespaceType, msg string) sdk.Error { return sdk.NewError(codespace, CodeInvalidMsgFormat, fmt.Sprintf("Invalid Msg Format: %s", msg)) } + +// ErrNoVotingInfoFound called when no voting info found +func ErrNoVotingInfoFound(codespace sdk.CodespaceType, valAddr sdk.ValAddress) sdk.Error { + return sdk.NewError(codespace, CodeMissingVotingInfo, fmt.Sprintf("no signing info found for address: %s", valAddr)) +} diff --git a/x/oracle/internal/types/events.go b/x/oracle/internal/types/events.go new file mode 100644 index 000000000..9438b6f4b --- /dev/null +++ b/x/oracle/internal/types/events.go @@ -0,0 +1,24 @@ +//noalias +package types + +// Oracle module event types +const ( + EventTypePriceUpdate = "price_update" + EventTypeSlash = "slash" + EventTypeLiveness = "liveness" + EventTypePrevote = "prevote" + EventTypeVote = "vote" + EventTypeFeedDeleate = "feed_delegate" + + AttributeKeyAddress = "address" + AttributeKeyHeight = "height" + AttributeKeyMissedVotes = "missed_votes" + AttributeKeyDenom = "denom" + AttributeKeyVoter = "voter" + AttributeKeyPower = "power" + AttributeKeyPrice = "price" + AttributeKeyOperator = "operator" + AttributeKeyFeeder = "feeder" + + AttributeValueCategory = ModuleName +) diff --git a/x/oracle/internal/types/expected_keeper.go b/x/oracle/internal/types/expected_keeper.go new file mode 100644 index 000000000..314a05f5d --- /dev/null +++ b/x/oracle/internal/types/expected_keeper.go @@ -0,0 +1,35 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// expected keeper for staking module +type StakingKeeper interface { + Validator(ctx sdk.Context, address sdk.ValAddress) stakingexported.ValidatorI // get validator by operator address; nil when validator not found + TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set + IterateBondedValidatorsByPower(sdk.Context, func(index int64, validator stakingexported.ValidatorI) (stop bool)) + Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction + MaxValidators(sdk.Context) uint16 // MaxValidators returns the maximum amount of bonded validators +} + +// expected keeper for distribution module +type DistributionKeeper interface { + AllocateTokensToValidator(ctx sdk.Context, val stakingexported.ValidatorI, tokens sdk.DecCoins) +} + +// expected keeper for supply module +type SupplyKeeper interface { + GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI + SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) + GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) + SetSupply(ctx sdk.Context, supply supplyexported.SupplyI) + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) sdk.Error +} + +// StakingHooks event hooks for staking validator object (noalias) +type StakingHooks interface { + AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is bonded +} diff --git a/x/oracle/internal/types/genesis.go b/x/oracle/internal/types/genesis.go new file mode 100644 index 000000000..36b771109 --- /dev/null +++ b/x/oracle/internal/types/genesis.go @@ -0,0 +1,65 @@ +package types + +import ( + "bytes" +) + +// GenesisState - all oracle state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` + VotingInfos map[string]VotingInfo `json:"voting_infos" yaml:"voting_infos"` + MissedVotes map[string][]MissedVote `json:"missed_votes" yaml:"missed_votes"` +} + +// NewGenesisState creates a new GenesisState object +func NewGenesisState( + params Params, votingInfo map[string]VotingInfo, MissedVotes map[string][]MissedVote, +) GenesisState { + + return GenesisState{ + Params: params, + VotingInfos: votingInfo, + MissedVotes: MissedVotes, + } +} + +// MissedVote +type MissedVote struct { + Index int64 `json:"index" yaml:"index"` + Missed bool `json:"missed" yaml:"missed"` +} + +// NewMissedVote creates a new MissedVote instance +func NewMissedVote(index int64, missed bool) MissedVote { + return MissedVote{ + Index: index, + Missed: missed, + } +} + +// DefaultGenesisState - default GenesisState used by columbus-2 +func DefaultGenesisState() GenesisState { + return GenesisState{ + Params: DefaultParams(), + VotingInfos: make(map[string]VotingInfo), + MissedVotes: make(map[string][]MissedVote), + } +} + +// ValidateGenesis validates the oracle genesis parameters +func ValidateGenesis(data GenesisState) error { + return data.Params.Validate() +} + +// Checks whether 2 GenesisState structs are equivalent. +func (data GenesisState) Equal(data2 GenesisState) bool { + b1 := ModuleCdc.MustMarshalBinaryBare(data) + b2 := ModuleCdc.MustMarshalBinaryBare(data2) + return bytes.Equal(b1, b2) +} + +// Returns if a GenesisState is empty or has data in it +func (data GenesisState) IsEmpty() bool { + emptyGenState := GenesisState{} + return data.Equal(emptyGenState) +} diff --git a/x/oracle/internal/types/genesis_test.go b/x/oracle/internal/types/genesis_test.go new file mode 100644 index 000000000..bc0b1b06f --- /dev/null +++ b/x/oracle/internal/types/genesis_test.go @@ -0,0 +1,37 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGenesisValidation(t *testing.T) { + genState := DefaultGenesisState() + require.NoError(t, ValidateGenesis(genState)) + + genState.Params.SlashFraction = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) + + genState.Params.SlashFraction = sdk.ZeroDec() + genState.Params.MinValidVotesPerWindow = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) + + genState.Params.MinValidVotesPerWindow = sdk.ZeroDec() + genState.Params.VotesWindow = 0 + require.Error(t, ValidateGenesis(genState)) +} + +func TestGenesisEqual(t *testing.T) { + genState1 := DefaultGenesisState() + genState2 := DefaultGenesisState() + + require.True(t, genState1.Equal(genState2)) +} + +func TestGenesisEmpty(t *testing.T) { + genState := GenesisState{} + require.True(t, genState.IsEmpty()) +} diff --git a/x/oracle/internal/types/keys.go b/x/oracle/internal/types/keys.go new file mode 100644 index 000000000..c2932ae8c --- /dev/null +++ b/x/oracle/internal/types/keys.go @@ -0,0 +1,88 @@ +package types + +import ( + "encoding/binary" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName is the name of the oracle module + ModuleName = "oracle" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // RouterKey is the msg router key for the oracle module + RouterKey = ModuleName + + // QuerierRoute is the query router key for the oracle module + QuerierRoute = ModuleName +) + +// Keys for oracle store +// Items are stored with the following key: values +// +// - 0x01: Prevote +// +// - 0x02: Vote +// +// - 0x03: sdk.Dec +// +// - 0x04: accAddress +// +// - 0x05: Claim +// +// - 0x06: bool +var ( + // Keys for store prefixes + PrevoteKey = []byte{0x01} // prefix for each key to a prevote + VoteKey = []byte{0x02} // prefix for each key to a vote + PriceKey = []byte{0x03} // prefix for each key to a price + FeederDelegationKey = []byte{0x04} // prefix for each key to a feeder delegation + ClaimKey = []byte{0x05} // prefix for each key to claim weight of oracle voter + MissedVoteBitArrayKey = []byte{0x06} // Prefix for missed vote bit array + VotingInfoKey = []byte{0x07} // Prefix for voting info +) + +// GetPrevoteKey - stored by *Validator* address and denom +func GetPrevoteKey(denom string, v sdk.ValAddress) []byte { + return append(append(PrevoteKey, []byte(denom)...), v.Bytes()...) +} + +// GetVoteKey - stored by *Validator* address and denom +func GetVoteKey(denom string, v sdk.ValAddress) []byte { + return append(append(VoteKey, []byte(denom)...), v.Bytes()...) +} + +// GetPriceKey - stored by *denom* +func GetPriceKey(denom string) []byte { + return append(PriceKey, []byte(denom)...) +} + +// GetFeederDelegationKey - stored by *Validator* address +func GetFeederDelegationKey(v sdk.ValAddress) []byte { + return append(FeederDelegationKey, v.Bytes()...) +} + +// GetClaimKey - stored by *Validator* address +func GetClaimKey(v sdk.ValAddress) []byte { + return append(ClaimKey, v.Bytes()...) +} + +// GetMissedVoteBitArrayPrefixKey - stored by *Validator* address +func GetMissedVoteBitArrayPrefixKey(v sdk.ValAddress) []byte { + return append(MissedVoteBitArrayKey, v.Bytes()...) +} + +// GetMissedVoteBitArrayKey - stored by *Validator* address +func GetMissedVoteBitArrayKey(v sdk.ValAddress, i int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(i)) + return append(GetMissedVoteBitArrayPrefixKey(v), b...) +} + +// GetVotingInfoKey - stored by *Validator* address +func GetVotingInfoKey(v sdk.ValAddress) []byte { + return append(VotingInfoKey, v.Bytes()...) +} diff --git a/x/oracle/msg.go b/x/oracle/internal/types/msgs.go similarity index 81% rename from x/oracle/msg.go rename to x/oracle/internal/types/msgs.go index 522c46dd2..27f22959b 100644 --- a/x/oracle/msg.go +++ b/x/oracle/internal/types/msgs.go @@ -1,4 +1,4 @@ -package oracle +package types import ( "encoding/hex" @@ -8,6 +8,13 @@ import ( "github.com/tendermint/tendermint/crypto/tmhash" ) +// ensure Msg interface compliance at compile time +var ( + _ sdk.Msg = &MsgDelegateFeederPermission{} + _ sdk.Msg = &MsgPricePrevote{} + _ sdk.Msg = &MsgPriceVote{} +) + //------------------------------------------------- //------------------------------------------------- @@ -15,10 +22,10 @@ import ( // The purpose of prevote is to hide vote price with hash // which is formatted as hex string in SHA256("salt:price:denom:voter") type MsgPricePrevote struct { - Hash string `json:"hash"` // hex string - Denom string `json:"denom"` - Feeder sdk.AccAddress `json:"feeder"` - Validator sdk.ValAddress `json:"validator"` + Hash string `json:"hash" yaml:"hash"` // hex string + Denom string `json:"denom" yaml:"denom"` + Feeder sdk.AccAddress `json:"feeder" yaml:"feeder"` + Validator sdk.ValAddress `json:"validator" yaml:"validator"` } // NewMsgPricePrevote creates a MsgPricePrevote instance @@ -39,7 +46,7 @@ func (msg MsgPricePrevote) Type() string { return "priceprevote" } // GetSignBytes implements sdk.Msg func (msg MsgPricePrevote) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } // GetSigners implements sdk.Msg @@ -83,11 +90,11 @@ func (msg MsgPricePrevote) String() string { // For example, if the validator believes that the effective price of Luna in USD is 10.39, that's // what the price field would be, and if 1213.34 for KRW, same. type MsgPriceVote struct { - Price sdk.Dec `json:"price"` // the effective price of Luna in {Denom} - Salt string `json:"salt"` - Denom string `json:"denom"` - Feeder sdk.AccAddress `json:"feeder"` - Validator sdk.ValAddress `json:"validator"` + Price sdk.Dec `json:"price" yaml:"price"` // the effective price of Luna in {Denom} + Salt string `json:"salt" yaml:"salt"` + Denom string `json:"denom" yaml:"denom"` + Feeder sdk.AccAddress `json:"feeder" yaml:"feeder"` + Validator sdk.ValAddress `json:"validator" yaml:"validator"` } // NewMsgPriceVote creates a MsgPriceVote instance @@ -109,7 +116,7 @@ func (msg MsgPriceVote) Type() string { return "pricevote" } // GetSignBytes implements sdk.Msg func (msg MsgPriceVote) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } // GetSigners implements sdk.Msg @@ -156,15 +163,15 @@ func (msg MsgPriceVote) String() string { // MsgDelegateFeederPermission - struct for delegating oracle voting rights to another address. type MsgDelegateFeederPermission struct { - Operator sdk.ValAddress `json:"operator"` - FeedDelegate sdk.AccAddress `json:"feed_delegate"` + Operator sdk.ValAddress `json:"operator" yaml:"operator"` + Delegatee sdk.AccAddress `json:"delegatee" yaml:"delegatee"` } // NewMsgDelegateFeederPermission creates a MsgDelegateFeederPermission instance func NewMsgDelegateFeederPermission(operatorAddress sdk.ValAddress, feederAddress sdk.AccAddress) MsgDelegateFeederPermission { return MsgDelegateFeederPermission{ - Operator: operatorAddress, - FeedDelegate: feederAddress, + Operator: operatorAddress, + Delegatee: feederAddress, } } @@ -176,7 +183,7 @@ func (msg MsgDelegateFeederPermission) Type() string { return "delegatefeeder" } // GetSignBytes implements sdk.Msg func (msg MsgDelegateFeederPermission) GetSignBytes() []byte { - return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } // GetSigners implements sdk.Msg @@ -190,7 +197,7 @@ func (msg MsgDelegateFeederPermission) ValidateBasic() sdk.Error { return sdk.ErrInvalidAddress("Invalid address: " + msg.Operator.String()) } - if msg.FeedDelegate.Empty() { + if msg.Delegatee.Empty() { return sdk.ErrInvalidAddress("Invalid address: " + msg.Operator.String()) } @@ -201,6 +208,6 @@ func (msg MsgDelegateFeederPermission) ValidateBasic() sdk.Error { func (msg MsgDelegateFeederPermission) String() string { return fmt.Sprintf(`MsgDelegateFeederPermission operator: %s, - feed_delegate: %s`, - msg.Operator, msg.FeedDelegate) + delegatee: %s`, + msg.Operator, msg.Delegatee) } diff --git a/x/oracle/msg_test.go b/x/oracle/internal/types/msgs_test.go similarity index 61% rename from x/oracle/msg_test.go rename to x/oracle/internal/types/msgs_test.go index 5af0c170c..abb470d74 100644 --- a/x/oracle/msg_test.go +++ b/x/oracle/internal/types/msgs_test.go @@ -1,12 +1,11 @@ -package oracle +package types import ( "encoding/hex" "testing" - "github.com/terra-project/core/types/assets" + core "github.com/terra-project/core/types" - "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/stretchr/testify/require" @@ -15,7 +14,7 @@ import ( func TestMsgPricePrevote(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) - bz, err := VoteHash("1", sdk.OneDec(), assets.MicroSDRDenom, types.ValAddress(addrs[0])) + bz, err := VoteHash("1", sdk.OneDec(), core.MicroSDRDenom, sdk.ValAddress(addrs[0])) require.Nil(t, err) tests := []struct { @@ -25,10 +24,10 @@ func TestMsgPricePrevote(t *testing.T) { expectPass bool }{ {hex.EncodeToString(bz), "", addrs[0], false}, - {hex.EncodeToString(bz), assets.MicroCNYDenom, addrs[0], true}, - {hex.EncodeToString(bz), assets.MicroCNYDenom, addrs[0], true}, - {hex.EncodeToString(bz), assets.MicroCNYDenom, sdk.AccAddress{}, false}, - {"", assets.MicroCNYDenom, addrs[0], false}, + {hex.EncodeToString(bz), core.MicroCNYDenom, addrs[0], true}, + {hex.EncodeToString(bz), core.MicroCNYDenom, addrs[0], true}, + {hex.EncodeToString(bz), core.MicroCNYDenom, sdk.AccAddress{}, false}, + {"", core.MicroCNYDenom, addrs[0], false}, } for i, tc := range tests { @@ -52,10 +51,10 @@ func TestMsgPriceVote(t *testing.T) { expectPass bool }{ {"", addrs[0], "123", sdk.OneDec(), false}, - {assets.MicroCNYDenom, addrs[0], "123", sdk.OneDec().MulInt64(assets.MicroUnit), true}, - {assets.MicroCNYDenom, addrs[0], "123", sdk.ZeroDec(), false}, - {assets.MicroCNYDenom, sdk.AccAddress{}, "123", sdk.OneDec().MulInt64(assets.MicroUnit), false}, - {assets.MicroCNYDenom, addrs[0], "", sdk.OneDec().MulInt64(assets.MicroUnit), false}, + {core.MicroCNYDenom, addrs[0], "123", sdk.OneDec().MulInt64(core.MicroUnit), true}, + {core.MicroCNYDenom, addrs[0], "123", sdk.ZeroDec(), false}, + {core.MicroCNYDenom, sdk.AccAddress{}, "123", sdk.OneDec().MulInt64(core.MicroUnit), false}, + {core.MicroCNYDenom, addrs[0], "", sdk.OneDec().MulInt64(core.MicroUnit), false}, } for i, tc := range tests { diff --git a/x/oracle/internal/types/params.go b/x/oracle/internal/types/params.go new file mode 100644 index 000000000..3fbddb005 --- /dev/null +++ b/x/oracle/internal/types/params.go @@ -0,0 +1,120 @@ +package types + +import ( + "fmt" + + core "github.com/terra-project/core/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/subspace" +) + +// DefaultParamspace +const DefaultParamspace = ModuleName + +// Parameter keys +var ( + ParamStoreKeyVotePeriod = []byte("voteperiod") + ParamStoreKeyVoteThreshold = []byte("votethreshold") + ParamStoreKeyRewardBand = []byte("rewardband") + ParamStoreKeyRewardFraction = []byte("rewardfraction") + ParamStoreKeyVotesWindow = []byte("voteswindow") + ParamStoreKeyMinValidVotesPerWindow = []byte("minvalidvotesperwindow") + ParamStoreKeySlashFraction = []byte("slashfraction") +) + +// Default parameter values +const ( + DefaultVotePeriod = core.BlocksPerMinute // 1 minute + DefaultVotesWindow = int64(1000) // 1000 oracle period +) + +// Default parameter values +var ( + DefaultVoteThreshold = sdk.NewDecWithPrec(50, 2) // 50% + DefaultRewardBand = sdk.NewDecWithPrec(1, 2) // 1% + DefaultRewardFraction = sdk.NewDecWithPrec(1, 2) // 1% + DefaultMinValidVotesPerWindow = sdk.NewDecWithPrec(5, 2) // 5% + DefaultSlashFraction = sdk.NewDecWithPrec(1, 4) // 0.01% +) + +var _ subspace.ParamSet = &Params{} + +// Params oracle parameters +type Params struct { + VotePeriod int64 `json:"vote_period" yaml:"vote_period"` + VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"` + RewardBand sdk.Dec `json:"reward_band" yaml:"reward_band"` + VotesWindow int64 `json:"votes_window" yaml:"votes_window"` + MinValidVotesPerWindow sdk.Dec `json:"min_valid_votes_per_window" yaml:"min_valid_votes_per_window"` + SlashFraction sdk.Dec `json:"slash_fraction" yaml:"slash_fraction"` + RewardFraction sdk.Dec `json:"reward_fraction" yaml:"reward_fraction"` +} + +// DefaultParams creates default oracle module parameters +func DefaultParams() Params { + return Params{ + VotePeriod: DefaultVotePeriod, + VoteThreshold: DefaultVoteThreshold, + RewardBand: DefaultRewardBand, + RewardFraction: DefaultRewardFraction, + VotesWindow: DefaultVotesWindow, + MinValidVotesPerWindow: DefaultMinValidVotesPerWindow, + SlashFraction: DefaultSlashFraction, + } +} + +// validate a set of params +func (params Params) Validate() error { + if params.VotePeriod <= 0 { + return fmt.Errorf("oracle parameter VotePeriod must be > 0, is %d", params.VotePeriod) + } + if params.VoteThreshold.LTE(sdk.NewDecWithPrec(33, 2)) { + return fmt.Errorf("oracle parameter VoteTheshold must be greater than 33 percent") + } + if params.RewardBand.IsNegative() { + return fmt.Errorf("oracle parameter RewardBand must be positive") + } + if params.RewardFraction.IsNegative() { + return fmt.Errorf("oracle parameter RewardBand must be positive") + } + if params.VotesWindow <= 10 { + return fmt.Errorf("oracle parameter VotesWindow must be > 0, is %d", params.VotesWindow) + } + if params.SlashFraction.GT(sdk.NewDecWithPrec(1, 2)) || params.SlashFraction.IsNegative() { + return fmt.Errorf("oracle parameter SlashFraction must be smaller or equal than 1 percent and positive") + } + if params.MinValidVotesPerWindow.IsNegative() || params.MinValidVotesPerWindow.GT(sdk.OneDec()) { + return fmt.Errorf("Min valid votes per window should be less than or equal to one and greater than zero, is %s", params.MinValidVotesPerWindow.String()) + } + return nil +} + +// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs +// pairs of oracle module's parameters. +// nolint +func (params *Params) ParamSetPairs() subspace.ParamSetPairs { + return subspace.ParamSetPairs{ + {Key: ParamStoreKeyVotePeriod, Value: ¶ms.VotePeriod}, + {Key: ParamStoreKeyVoteThreshold, Value: ¶ms.VoteThreshold}, + {Key: ParamStoreKeyRewardBand, Value: ¶ms.RewardBand}, + {Key: ParamStoreKeyRewardFraction, Value: ¶ms.RewardFraction}, + {Key: ParamStoreKeyVotesWindow, Value: ¶ms.VotesWindow}, + {Key: ParamStoreKeyMinValidVotesPerWindow, Value: ¶ms.MinValidVotesPerWindow}, + {Key: ParamStoreKeySlashFraction, Value: ¶ms.SlashFraction}, + } +} + +// implements fmt.Stringer +func (params Params) String() string { + return fmt.Sprintf(`Treasury Params: + VotePeriod: %d + VoteThreshold: %s + RewardBand: %s + RewardFraction: %s + VotesWindow: %d + MinValidVotesPerWindow: %s + SlashFraction: %s + `, params.VotePeriod, params.VoteThreshold, params.RewardBand, params.RewardFraction, + params.VotesWindow, params.MinValidVotesPerWindow, params.SlashFraction) +} diff --git a/x/oracle/internal/types/params_test.go b/x/oracle/internal/types/params_test.go new file mode 100644 index 000000000..b9a53c787 --- /dev/null +++ b/x/oracle/internal/types/params_test.go @@ -0,0 +1,50 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestParamsEqual(t *testing.T) { + p1 := DefaultParams() + err := p1.Validate() + require.NoError(t, err) + + // minus vote period + p1.VotePeriod = -1 + err = p1.Validate() + require.Error(t, err) + + // small vote threshold + p2 := DefaultParams() + p2.VoteThreshold = sdk.ZeroDec() + err = p2.Validate() + require.Error(t, err) + + // negative reward band + p3 := DefaultParams() + p3.RewardBand = sdk.NewDecWithPrec(-1, 2) + err = p3.Validate() + require.Error(t, err) + + // negative reward fraction + p4 := DefaultParams() + p4.RewardFraction = sdk.NewDecWithPrec(-1, 2) + err = p4.Validate() + require.Error(t, err) + + // zero slash window + p5 := DefaultParams() + p5.VotesWindow = 0 + err = p5.Validate() + require.Error(t, err) + + // negative slash fraction + p6 := DefaultParams() + p6.SlashFraction = sdk.NewDecWithPrec(-1, 2) + err = p6.Validate() + require.Error(t, err) +} diff --git a/x/oracle/internal/types/querier.go b/x/oracle/internal/types/querier.go new file mode 100644 index 000000000..6bffa19f0 --- /dev/null +++ b/x/oracle/internal/types/querier.go @@ -0,0 +1,78 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + QueryParameters = "parameters" + QueryPrice = "price" + QueryActives = "actives" + QueryPrevotes = "prevotes" + QueryVotes = "votes" + QueryFeederDelegation = "feederDelegation" + QueryVotingInfo = "signingInfo" + QueryVotingInfos = "signingInfos" +) + +// QueryPriceParams defines the params for the following queries: +// - 'custom/oracle/price' +type QueryPriceParams struct { + Denom string +} + +func NewQueryPriceParams(denom string) QueryPriceParams { + return QueryPriceParams{denom} +} + +// QueryPrevotesParams defines the params for the following queries: +// - 'custom/oracle/prevotes' +type QueryPrevotesParams struct { + Voter sdk.ValAddress + Denom string +} + +func NewQueryPrevotesParams(voter sdk.ValAddress, denom string) QueryPrevotesParams { + return QueryPrevotesParams{voter, denom} +} + +// QueryVotesParams defines the params for the following queries: +// - 'custom/oracle/votes' +type QueryVotesParams struct { + Voter sdk.ValAddress + Denom string +} + +func NewQueryVotesParams(voter sdk.ValAddress, denom string) QueryVotesParams { + return QueryVotesParams{voter, denom} +} + +// QueryFeederDelegationParams defeins the params for the following queries: +// - 'custom/oracle/feederDelegation' +type QueryFeederDelegationParams struct { + Validator sdk.ValAddress +} + +func NewQueryFeederDelegationParams(validator sdk.ValAddress) QueryFeederDelegationParams { + return QueryFeederDelegationParams{validator} +} + +// QueryVotingInfoParams defines the params for the following queries: +// - 'custom/oracle/votingInfo' +type QueryVotingInfoParams struct { + ValAddress sdk.ValAddress +} + +func NewQueryVotingInfoParams(valAddr sdk.ValAddress) QueryVotingInfoParams { + return QueryVotingInfoParams{valAddr} +} + +// QueryVotingInfosParams defines the params for the following queries: +// - 'custom/oracle/votingInfos' +type QueryVotingInfosParams struct { + Page, Limit int +} + +func NewQueryVotingInfosParams(page, limit int) QueryVotingInfosParams { + return QueryVotingInfosParams{page, limit} +} diff --git a/x/oracle/internal/types/test_utils.go b/x/oracle/internal/types/test_utils.go new file mode 100644 index 000000000..6376e1c61 --- /dev/null +++ b/x/oracle/internal/types/test_utils.go @@ -0,0 +1,126 @@ +// nolint:deadcode unused noalias +package types + +import ( + "math" + "math/rand" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +const oracleDecPrecision = 6 + +// GenerateRandomTestCase nolint +func GenerateRandomTestCase() (prices []float64, valValAddrs []sdk.ValAddress, stakingKeeper DummyStakingKeeper) { + valValAddrs = []sdk.ValAddress{} + mockValidators := []MockValidator{} + + base := math.Pow10(oracleDecPrecision) + + rand.Seed(int64(time.Now().Nanosecond())) + numInputs := 10 + (rand.Int() % 100) + for i := 0; i < numInputs; i++ { + price := float64(int64(rand.Float64()*base)) / base + prices = append(prices, price) + + pubKey := secp256k1.GenPrivKey().PubKey() + valValAddr := sdk.ValAddress(pubKey.Address()) + valValAddrs = append(valValAddrs, valValAddr) + + power := rand.Int63()%1000 + 1 + mockValidator := NewMockValidator(valValAddr, power) + mockValidators = append(mockValidators, mockValidator) + } + + stakingKeeper = NewDummyStakingKeeper(mockValidators) + + return +} + +var _ StakingKeeper = DummyStakingKeeper{} + +// DummyStakingKeeper dummy staking keeper to test ballot +type DummyStakingKeeper struct { + validators []MockValidator +} + +func NewDummyStakingKeeper(validators []MockValidator) DummyStakingKeeper { + return DummyStakingKeeper{ + validators: validators, + } +} + +// MaxValidators nolint +func (sk DummyStakingKeeper) MaxValidators(_ sdk.Context) uint16 { + return 100 +} + +// Validators nolint +func (sk DummyStakingKeeper) Validators() []MockValidator { + return sk.validators +} + +// Validator nolint +func (sk DummyStakingKeeper) Validator(ctx sdk.Context, address sdk.ValAddress) exported.ValidatorI { + for _, validator := range sk.validators { + if validator.GetOperator().Equals(address) { + return validator + } + } + + return nil +} + +// TotalBondedTokens nolint +func (DummyStakingKeeper) TotalBondedTokens(_ sdk.Context) sdk.Int { + return sdk.ZeroInt() +} + +// IterateBondedValidatorsByPower nolint +func (DummyStakingKeeper) IterateBondedValidatorsByPower(_ sdk.Context, _ func(_ int64, _ exported.ValidatorI) (stop bool)) { +} + +// Slash nolint +func (DummyStakingKeeper) Slash(_ sdk.Context, _ sdk.ConsAddress, _, _ int64, _ sdk.Dec) {} + +type MockValidator struct { + power int64 + operator sdk.ValAddress +} + +var _ exported.ValidatorI = MockValidator{} + +func (MockValidator) IsJailed() bool { return false } +func (MockValidator) GetMoniker() string { return "" } +func (MockValidator) GetStatus() sdk.BondStatus { return sdk.Bonded } +func (MockValidator) IsBonded() bool { return true } +func (MockValidator) IsUnbonded() bool { return false } +func (MockValidator) IsUnbonding() bool { return false } +func (v MockValidator) GetOperator() sdk.ValAddress { return v.operator } +func (MockValidator) GetConsPubKey() crypto.PubKey { return nil } +func (MockValidator) GetConsAddr() sdk.ConsAddress { return nil } +func (v MockValidator) GetTokens() sdk.Int { return sdk.TokensFromConsensusPower(v.power) } +func (v MockValidator) GetBondedTokens() sdk.Int { return sdk.TokensFromConsensusPower(v.power) } +func (v MockValidator) GetConsensusPower() int64 { return v.power } +func (v MockValidator) GetCommission() sdk.Dec { return sdk.ZeroDec() } +func (v MockValidator) GetMinSelfDelegation() sdk.Int { return sdk.OneInt() } +func (v MockValidator) GetDelegatorShares() sdk.Dec { return sdk.NewDec(v.power) } +func (v MockValidator) TokensFromShares(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } +func (v MockValidator) TokensFromSharesTruncated(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } +func (v MockValidator) TokensFromSharesRoundUp(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } +func (v MockValidator) SharesFromTokens(amt sdk.Int) (sdk.Dec, sdk.Error) { return sdk.ZeroDec(), nil } +func (v MockValidator) SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, sdk.Error) { + return sdk.ZeroDec(), nil +} +func (v MockValidator) SetPower(power int64) { v.power = power } + +func NewMockValidator(valAddr sdk.ValAddress, power int64) MockValidator { + return MockValidator{ + power: power, + operator: valAddr, + } +} diff --git a/x/oracle/vote.go b/x/oracle/internal/types/vote.go similarity index 89% rename from x/oracle/vote.go rename to x/oracle/internal/types/vote.go index e5459ce48..ad9af7e2f 100644 --- a/x/oracle/vote.go +++ b/x/oracle/internal/types/vote.go @@ -1,4 +1,4 @@ -package oracle +package types import ( "fmt" @@ -70,12 +70,13 @@ func NewPriceVote(price sdk.Dec, denom string, voter sdk.ValAddress) PriceVote { } } -func (pv PriceVote) getPower(ctx sdk.Context, valset sdk.ValidatorSet) (sdk.Int, sdk.Error) { - if validator := valset.Validator(ctx, pv.Voter); validator != nil { - return validator.GetBondedTokens(), nil +func (pv PriceVote) getPower(ctx sdk.Context, sk StakingKeeper) int64 { + validator := sk.Validator(ctx, pv.Voter) + if validator == nil { + return 0 } - return sdk.ZeroInt(), ErrVoterNotValidator(DefaultCodespace, pv.Voter) + return validator.GetConsensusPower() } // String implements fmt.Stringer diff --git a/x/oracle/internal/types/voting_info.go b/x/oracle/internal/types/voting_info.go new file mode 100644 index 000000000..8072ea451 --- /dev/null +++ b/x/oracle/internal/types/voting_info.go @@ -0,0 +1,40 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// VotingInfo defines the voting info for a validator +type VotingInfo struct { + Address sdk.ValAddress `json:"address" yaml:"address"` // validator consensus address + StartHeight int64 `json:"start_height" yaml:"start_height"` // height at which validator was first a candidate OR was unjailed + IndexOffset int64 `json:"index_offset" yaml:"index_offset"` // index offset into signed block bit array + MissedVotesCounter int64 `json:"missed_votes_counter" yaml:"missed_votes_counter"` // missed blocks counter (to avoid scanning the array every time) +} + +// NewVotingInfo creates a new NewVotingInfo instance +func NewVotingInfo( + valAddr sdk.ValAddress, startHeight, + indexOffset, missedvotesCounter int64, +) VotingInfo { + + return VotingInfo{ + Address: valAddr, + StartHeight: startHeight, + IndexOffset: indexOffset, + MissedVotesCounter: missedvotesCounter, + } +} + +// String implements the stringer interface for NewVotingInfo +func (i VotingInfo) String() string { + return fmt.Sprintf(`Validator Signing Info: + Address: %s + Start Height: %d + Index Offset: %d + Missed Votes Counter: %d`, + i.Address, i.StartHeight, + i.IndexOffset, i.MissedVotesCounter) +} diff --git a/x/oracle/keeper.go b/x/oracle/keeper.go deleted file mode 100644 index db425d8b0..000000000 --- a/x/oracle/keeper.go +++ /dev/null @@ -1,344 +0,0 @@ -package oracle - -import ( - "strings" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper of the oracle store -type Keeper struct { - cdc *codec.Codec - key sdk.StoreKey - - mk MintKeeper - dk DistributionKeeper - fck FeeCollectionKeeper - - valset sdk.ValidatorSet - paramSpace params.Subspace -} - -// NewKeeper constructs a new keeper for oracle -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, mk MintKeeper, dk DistributionKeeper, fck FeeCollectionKeeper, - valset sdk.ValidatorSet, paramspace params.Subspace) Keeper { - return Keeper{ - cdc: cdc, - key: key, - - mk: mk, - dk: dk, - fck: fck, - - valset: valset, - paramSpace: paramspace.WithKeyTable(paramKeyTable()), - } -} - -//----------------------------------- -// Prevote logic - -// Iterate over prevotes in the store -func (k Keeper) iteratePrevotes(ctx sdk.Context, handler func(prevote PricePrevote) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixPrevote) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var prevote PricePrevote - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &prevote) - if handler(prevote) { - break - } - } -} - -// Iterate over votes in the store -func (k Keeper) iteratePrevotesWithPrefix(ctx sdk.Context, prefix []byte, handler func(vote PricePrevote) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefix) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var prevote PricePrevote - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &prevote) - if handler(prevote) { - break - } - } -} - -//----------------------------------- -// Votes logic - -// collectVotes collects all oracle votes for the period, categorized by the votes' denom parameter -func (k Keeper) collectVotes(ctx sdk.Context) (votes map[string]PriceBallot) { - votes = map[string]PriceBallot{} - handler := func(vote PriceVote) (stop bool) { - votes[vote.Denom] = append(votes[vote.Denom], vote) - return false - } - k.iterateVotes(ctx, handler) - - return -} - -// Iterate over votes in the store -func (k Keeper) iterateVotes(ctx sdk.Context, handler func(vote PriceVote) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixVote) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var vote PriceVote - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vote) - if handler(vote) { - break - } - } -} - -// Iterate over votes in the store -func (k Keeper) iterateVotesWithPrefix(ctx sdk.Context, prefix []byte, handler func(vote PriceVote) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefix) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - var vote PriceVote - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vote) - if handler(vote) { - break - } - } -} - -// Retrieves a prevote from the store -func (k Keeper) getPrevote(ctx sdk.Context, denom string, voter sdk.ValAddress) (prevote PricePrevote, err sdk.Error) { - store := ctx.KVStore(k.key) - b := store.Get(keyPrevote(denom, voter)) - if b == nil { - err = ErrNoPrevote(DefaultCodespace, voter, denom) - return - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &prevote) - return -} - -// Add a prevote to the store -func (k Keeper) addPrevote(ctx sdk.Context, prevote PricePrevote) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(prevote) - store.Set(keyPrevote(prevote.Denom, prevote.Voter), bz) -} - -// Delete a prevote from the store -func (k Keeper) deletePrevote(ctx sdk.Context, prevote PricePrevote) { - store := ctx.KVStore(k.key) - store.Delete(keyPrevote(prevote.Denom, prevote.Voter)) -} - -// Retrieves a vote from the store -func (k Keeper) getVote(ctx sdk.Context, denom string, voter sdk.ValAddress) (vote PriceVote, err sdk.Error) { - store := ctx.KVStore(k.key) - b := store.Get(keyVote(denom, voter)) - if b == nil { - err = ErrNoVote(DefaultCodespace, voter, denom) - return - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &vote) - return -} - -// Add a vote to the store -func (k Keeper) addVote(ctx sdk.Context, vote PriceVote) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(vote) - store.Set(keyVote(vote.Denom, vote.Voter), bz) -} - -// Delete a vote from the store -func (k Keeper) deleteVote(ctx sdk.Context, vote PriceVote) { - store := ctx.KVStore(k.key) - store.Delete(keyVote(vote.Denom, vote.Voter)) -} - -//----------------------------------- -// Price logic - -// GetLunaSwapRate gets the consensus exchange rate of Luna denominated in the denom asset from the store. -func (k Keeper) GetLunaSwapRate(ctx sdk.Context, denom string) (price sdk.Dec, err sdk.Error) { - if denom == assets.MicroLunaDenom { - return sdk.OneDec(), nil - } - - store := ctx.KVStore(k.key) - b := store.Get(keyPrice(denom)) - if b == nil { - return sdk.ZeroDec(), ErrUnknownDenomination(DefaultCodespace, denom) - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &price) - return -} - -// SetLunaSwapRate sets the consensus exchange rate of Luna denominated in the denom asset to the store. -func (k Keeper) SetLunaSwapRate(ctx sdk.Context, denom string, price sdk.Dec) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(price) - store.Set(keyPrice(denom), bz) -} - -// deletePrice deletes the consensus exchange rate of Luna denominated in the denom asset from the store. -func (k Keeper) deletePrice(ctx sdk.Context, denom string) { - store := ctx.KVStore(k.key) - store.Delete(keyPrice(denom)) -} - -// Get all active oracle asset denoms from the store -func (k Keeper) getActiveDenoms(ctx sdk.Context) (denoms DenomList) { - denoms = DenomList{} - - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixPrice) - for ; iter.Valid(); iter.Next() { - n := len(prefixPrice) + 1 - denom := string(iter.Key()[n:]) - denoms = append(denoms, denom) - } - iter.Close() - - return -} - -//----------------------------------- -// Params logic - -// GetParams get oracle params from the global param store -func (k Keeper) GetParams(ctx sdk.Context) Params { - var params Params - k.paramSpace.Get(ctx, paramStoreKeyParams, ¶ms) - return params -} - -// SetParams set oracle params from the global param store -func (k Keeper) SetParams(ctx sdk.Context, params Params) { - k.paramSpace.Set(ctx, paramStoreKeyParams, ¶ms) -} - -//----------------------------------- -// Feeder delegation logic - -// GetFeedDelegate gets the account address that the feeder right was delegated to by the validator operator. -func (k Keeper) GetFeedDelegate(ctx sdk.Context, operator sdk.ValAddress) (delegate sdk.AccAddress) { - store := ctx.KVStore(k.key) - b := store.Get(keyFeederDelegation(operator)) - if b == nil { - // By default the right is delegated to the validator itself - return sdk.AccAddress(operator) - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &delegate) - return -} - -// SetFeedDelegate sets the account address that the feeder right was delegated to by the validator operator. -func (k Keeper) SetFeedDelegate(ctx sdk.Context, operator sdk.ValAddress, delegatedFeeder sdk.AccAddress) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(delegatedFeeder) - store.Set(keyFeederDelegation(operator), bz) -} - -// Iterate over feeder delegations in the store -func (k Keeper) iterateFeederDelegations(ctx sdk.Context, handler func(delegate sdk.AccAddress, operator sdk.ValAddress) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixFeederDelegation) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - operatorAddress := strings.Split(string(iter.Key()), ":")[1] - operator, _ := sdk.ValAddressFromBech32(operatorAddress) - - var delegate sdk.AccAddress - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &delegate) - if handler(delegate, operator) { - break - } - } -} - -//----------------------------------- -// Swap fee pool logic - -// GetSwapFeePool retrieves the swap fee pool from the store -func (k Keeper) GetSwapFeePool(ctx sdk.Context) (pool sdk.Coins) { - store := ctx.KVStore(k.key) - b := store.Get(keySwapFeePool) - if b == nil { - return sdk.Coins{} - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &pool) - return -} - -// setSwapFeePool sets the swap fee pool to the store -func (k Keeper) AddSwapFeePool(ctx sdk.Context, fees sdk.Coins) { - pool := k.GetSwapFeePool(ctx) - pool = pool.Add(fees) - - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(pool) - store.Set(keySwapFeePool, bz) -} - -// clearSwapFeePool clears the swap fee pool from the store -func (k Keeper) clearSwapFeePool(ctx sdk.Context) { - store := ctx.KVStore(k.key) - store.Delete(keySwapFeePool) -} - -//----------------------------------- -// Claim pool logic - -// Iterate over oracle reward claims in the store -func (k Keeper) iterateClaimPool(ctx sdk.Context, handler func(recipient sdk.AccAddress, weight sdk.Int) (stop bool)) { - store := ctx.KVStore(k.key) - iter := sdk.KVStorePrefixIterator(store, prefixClaim) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - recipientAddress := strings.Split(string(iter.Key()), ":")[1] - recipient, _ := sdk.AccAddressFromBech32(recipientAddress) - - var weight sdk.Int - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &weight) - if handler(recipient, weight) { - break - } - } -} - -// addClaimPool adds a claim to the the claim pool in the store -func (k Keeper) addClaimPool(ctx sdk.Context, pool types.ClaimPool) { - store := ctx.KVStore(k.key) - - for _, claim := range pool { - storeKeyClaim := keyClaim(claim.Recipient) - b := store.Get(storeKeyClaim) - weight := claim.Weight - if b != nil { - var prevWeight sdk.Int - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &prevWeight) - - weight = weight.Add(prevWeight) - } - b = k.cdc.MustMarshalBinaryLengthPrefixed(weight) - store.Set(storeKeyClaim, b) - } -} - -// clearClaimPool clears the claim pool from the store -func (k Keeper) clearClaimPool(ctx sdk.Context) { - store := ctx.KVStore(k.key) - k.iterateClaimPool(ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - store.Delete(keyClaim(recipient)) - return false - }) -} diff --git a/x/oracle/keeper_keys.go b/x/oracle/keeper_keys.go deleted file mode 100644 index c6b3a6e6f..000000000 --- a/x/oracle/keeper_keys.go +++ /dev/null @@ -1,50 +0,0 @@ -package oracle - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -var ( - prefixPrevote = []byte("prevote") - prefixVote = []byte("vote") - prefixPrice = []byte("price") - prefixDropCounter = []byte("drop") - paramStoreKeyParams = []byte("params") - prefixFeederDelegation = []byte("feederdelegation") - prefixClaim = []byte("claim") - - keySwapFeePool = []byte("swapfeepool") -) - -func keyPrevote(denom string, voter sdk.ValAddress) []byte { - return []byte(fmt.Sprintf("%s:%s:%s", prefixPrevote, denom, voter)) -} - -func keyVote(denom string, voter sdk.ValAddress) []byte { - return []byte(fmt.Sprintf("%s:%s:%s", prefixVote, denom, voter)) -} - -func keyPrice(denom string) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixPrice, denom)) -} - -func keyClaim(recipient sdk.AccAddress) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixClaim, recipient)) -} - -func keyDropCounter(denom string) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixDropCounter, denom)) -} - -func paramKeyTable() params.KeyTable { - return params.NewKeyTable( - paramStoreKeyParams, Params{}, - ) -} - -func keyFeederDelegation(delegate sdk.ValAddress) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixFeederDelegation, delegate)) -} diff --git a/x/oracle/keeper_test.go b/x/oracle/keeper_test.go deleted file mode 100644 index c8ea6067d..000000000 --- a/x/oracle/keeper_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package oracle - -import ( - "encoding/hex" - - "github.com/terra-project/core/types" - "github.com/terra-project/core/types/assets" - - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestKeeperPrice(t *testing.T) { - input := createTestInput(t) - - cnyPrice := sdk.NewDecWithPrec(839, int64(oracleDecPrecision)).MulInt64(assets.MicroUnit) - gbpPrice := sdk.NewDecWithPrec(4995, int64(oracleDecPrecision)).MulInt64(assets.MicroUnit) - krwPrice := sdk.NewDecWithPrec(2838, int64(oracleDecPrecision)).MulInt64(assets.MicroUnit) - lunaPrice := sdk.NewDecWithPrec(3282384, int64(oracleDecPrecision)).MulInt64(assets.MicroUnit) - - // Set & get prices - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, cnyPrice) - price, err := input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroCNYDenom) - require.Nil(t, err) - require.Equal(t, cnyPrice, price) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, gbpPrice) - price, err = input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroGBPDenom) - require.Nil(t, err) - require.Equal(t, gbpPrice, price) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, krwPrice) - price, err = input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroKRWDenom) - require.Nil(t, err) - require.Equal(t, krwPrice, price) - - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroLunaDenom, lunaPrice) - price, _ = input.oracleKeeper.GetLunaSwapRate(input.ctx, assets.MicroLunaDenom) - require.Equal(t, sdk.OneDec(), price) -} - -func TestKeeperSwapPool(t *testing.T) { - input := createTestInput(t) - - // Test AddSwapFeePool - fees := sdk.NewCoins(sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1000))) - input.oracleKeeper.AddSwapFeePool(input.ctx, fees) - - // Test GetSwapFeePool - feesQuery := input.oracleKeeper.GetSwapFeePool(input.ctx) - require.Equal(t, fees, feesQuery) - - // Test clearSwapFeePool - input.oracleKeeper.clearSwapFeePool(input.ctx) - feesQuery = input.oracleKeeper.GetSwapFeePool(input.ctx) - - require.True(t, feesQuery.Empty()) -} - -func TestKeeperClaimPool(t *testing.T) { - input := createTestInput(t) - - // Test addClaimPool - claim := types.NewClaim(sdk.NewInt(10), addrs[0]) - claim2 := types.NewClaim(sdk.NewInt(20), addrs[1]) - claimPool := types.ClaimPool{claim, claim2} - input.oracleKeeper.addClaimPool(input.ctx, claimPool) - - claim = types.NewClaim(sdk.NewInt(15), addrs[0]) - claim2 = types.NewClaim(sdk.NewInt(30), addrs[2]) - claimPool = types.ClaimPool{claim, claim2} - input.oracleKeeper.addClaimPool(input.ctx, claimPool) - - // Test iterateClaimPool - input.oracleKeeper.iterateClaimPool(input.ctx, func(recipient sdk.AccAddress, weight sdk.Int) (stop bool) { - if recipient.Equals(addrs[0]) { - require.Equal(t, sdk.NewInt(25), weight) - } else if recipient.Equals(addrs[1]) { - require.Equal(t, sdk.NewInt(20), weight) - } else if recipient.Equals(addrs[2]) { - require.Equal(t, sdk.NewInt(30), weight) - } - return false - }) -} -func TestKeeperVote(t *testing.T) { - input := createTestInput(t) - - // Test addVote - vote := NewPriceVote(sdk.OneDec(), assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - input.oracleKeeper.addVote(input.ctx, vote) - - // Test getVote - voteQuery, err := input.oracleKeeper.getVote(input.ctx, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - require.Nil(t, err) - require.Equal(t, vote, voteQuery) - - // Test iteratevotes - input.oracleKeeper.iterateVotes(input.ctx, func(vote PriceVote) bool { - require.Equal(t, vote, voteQuery) - return true - }) - - // Test collectvotes - votes := input.oracleKeeper.collectVotes(input.ctx) - require.True(t, len(votes) == 1) - require.True(t, len(votes[assets.MicroSDRDenom]) == 1) - require.Equal(t, vote, votes[assets.MicroSDRDenom][0]) - - // Test deletevote - input.oracleKeeper.deleteVote(input.ctx, vote) - _, err = input.oracleKeeper.getVote(input.ctx, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - require.NotNil(t, err) -} - -func TestKeeperPrevote(t *testing.T) { - input := createTestInput(t) - - hash, _ := VoteHash("1234", sdk.OneDec(), assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - hexHas := hex.EncodeToString(hash) - - // Test addPrevote - prevote := NewPricePrevote(hexHas, assets.MicroSDRDenom, sdk.ValAddress(addrs[0]), 1) - input.oracleKeeper.addPrevote(input.ctx, prevote) - - // Test getPrevote - prevoteQuery, err := input.oracleKeeper.getPrevote(input.ctx, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - require.Nil(t, err) - require.Equal(t, prevote, prevoteQuery) - - // Test iteratevotes - input.oracleKeeper.iteratePrevotes(input.ctx, func(prevote PricePrevote) bool { - require.Equal(t, prevote, prevoteQuery) - return true - }) - - // Test deletevote - input.oracleKeeper.deletePrevote(input.ctx, prevote) - _, err = input.oracleKeeper.getPrevote(input.ctx, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - require.NotNil(t, err) -} - -func TestKeeperParams(t *testing.T) { - input := createTestInput(t) - - // Test default params setting - input.oracleKeeper.SetParams(input.ctx, DefaultParams()) - params := input.oracleKeeper.GetParams(input.ctx) - require.NotNil(t, params) - - // Test custom params setting - votePeriod := int64(10) - voteThreshold := sdk.NewDecWithPrec(1, 10) - oracleRewardBand := sdk.NewDecWithPrec(1, 2) - - // Should really test validateParams, but skipping because obvious - newParams := NewParams(votePeriod, voteThreshold, oracleRewardBand) - input.oracleKeeper.SetParams(input.ctx, newParams) - - storedParams := input.oracleKeeper.GetParams(input.ctx) - require.NotNil(t, storedParams) - require.Equal(t, newParams, storedParams) -} - -func TestKeeperFeederDelegation(t *testing.T) { - input := createTestInput(t) - - // Test default getters and setters - delegate := input.oracleKeeper.GetFeedDelegate(input.ctx, sdk.ValAddress(addrs[0])) - require.Equal(t, delegate, addrs[0]) - - input.oracleKeeper.SetFeedDelegate(input.ctx, sdk.ValAddress(addrs[0]), addrs[1]) - delegate = input.oracleKeeper.GetFeedDelegate(input.ctx, sdk.ValAddress(addrs[0])) - require.Equal(t, delegate, addrs[1]) -} diff --git a/x/oracle/log.go b/x/oracle/log.go deleted file mode 100644 index 283baceef..000000000 --- a/x/oracle/log.go +++ /dev/null @@ -1,28 +0,0 @@ -package oracle - -import ( - "encoding/json" -) - -const ( - // LogKeyPrice is to record treasury tax for a pay msg - LogKeyPrice = string("price") -) - -// Log is map type object to organize msg result -type Log map[string]string - -func NewLog() Log { - return Log{} -} - -func (log Log) append(key, value string) Log { - log[key] = value - - return log -} - -func (log Log) String() string { - jsonMap, _ := json.Marshal(log) - return string(jsonMap) -} diff --git a/x/oracle/module.go b/x/oracle/module.go new file mode 100644 index 000000000..4994fa1bc --- /dev/null +++ b/x/oracle/module.go @@ -0,0 +1,129 @@ +package oracle + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/terra-project/core/x/oracle/client/cli" + "github.com/terra-project/core/x/oracle/client/rest" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return ModuleName +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(cdc) +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + } +} + +// module name +func (AppModule) Name() string { return ModuleName } + +// register invariants +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// module message route name +func (AppModule) Route() string { return RouterKey } + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// module querier route name +func (AppModule) QuerierRoute() string { return RouterKey } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, genesisState) + + return []abci.ValidatorUpdate{} +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + genesisState := ExportGenesis(ctx, am.keeper) + data := ModuleCdc.MustMarshalJSON(genesisState) + return data +} + +// module begin-block +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// module end-block this should be called before staking module's EndBlock +// staking EndBlock will apply oracle slash validator update +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + EndBlocker(ctx, am.keeper) + return []abci.ValidatorUpdate{} +} diff --git a/x/oracle/params.go b/x/oracle/params.go deleted file mode 100644 index a90a16159..000000000 --- a/x/oracle/params.go +++ /dev/null @@ -1,55 +0,0 @@ -package oracle - -import ( - "fmt" - - "github.com/terra-project/core/types/util" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Params oracle parameters -type Params struct { - VotePeriod int64 `json:"vote_period"` // voting period in block height; tallys and reward claim period - VoteThreshold sdk.Dec `json:"vote_threshold"` // minimum stake power threshold to update price - OracleRewardBand sdk.Dec `json:"oracle_reward_band"` // band around the oracle weighted median to reward -} - -// NewParams creates a new param instance -func NewParams(votePeriod int64, voteThreshold sdk.Dec, oracleRewardBand sdk.Dec) Params { - return Params{ - VotePeriod: votePeriod, - VoteThreshold: voteThreshold, - OracleRewardBand: oracleRewardBand, - } -} - -// DefaultParams creates default oracle module parameters -func DefaultParams() Params { - return NewParams( - util.BlocksPerMinute, // 1 minute - sdk.NewDecWithPrec(50, 2), // 50% - sdk.NewDecWithPrec(1, 2), // 1% - ) -} - -func validateParams(params Params) error { - if params.VotePeriod <= 0 { - return fmt.Errorf("oracle parameter VotePeriod must be > 0, is %d", params.VotePeriod) - } - if params.VoteThreshold.LTE(sdk.NewDecWithPrec(33, 2)) { - return fmt.Errorf("oracle parameter VoteTheshold must be greater than 33 percent") - } - if params.OracleRewardBand.IsNegative() { - return fmt.Errorf("oracle parameter OracleRewardBand must be positive") - } - return nil -} - -func (params Params) String() string { - return fmt.Sprintf(`Oracle Params: - VotePeriod: %d - VoteThreshold: %s - OracleRewardBand: %s - `, params.VotePeriod, params.VoteThreshold, params.OracleRewardBand) -} diff --git a/x/oracle/querier.go b/x/oracle/querier.go deleted file mode 100644 index 0cc7396fa..000000000 --- a/x/oracle/querier.go +++ /dev/null @@ -1,261 +0,0 @@ -package oracle - -import ( - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" -) - -// query endpoints supported by the oracle Querier -const ( - QueryPrice = "price" - QueryVotes = "votes" - QueryPrevotes = "prevotes" - QueryActive = "active" - QueryParams = "params" - QueryFeederDelegation = "feeder" -) - -// NewQuerier is the module level router for state queries -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case QueryPrice: - return queryPrice(ctx, path[1:], req, keeper) - case QueryActive: - return queryActive(ctx, req, keeper) - case QueryVotes: - return queryVotes(ctx, req, keeper) - case QueryPrevotes: - return queryPrevotes(ctx, req, keeper) - case QueryParams: - return queryParams(ctx, req, keeper) - case QueryFeederDelegation: - return queryFeederDelegation(ctx, req, keeper) - default: - return nil, sdk.ErrUnknownRequest("unknown oracle query endpoint") - } - } -} - -// JSON response format -type QueryPriceResponse struct { - Price sdk.Dec `json:"price"` -} - -func (r QueryPriceResponse) String() (out string) { - out = r.Price.String() - return strings.TrimSpace(out) -} - -func queryPrice(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - denom := path[0] - - price, err := keeper.GetLunaSwapRate(ctx, denom) - if err != nil { - return nil, ErrUnknownDenomination(DefaultCodespace, denom) - } - - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, QueryPriceResponse{Price: price}) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) - } - - return bz, nil -} - -// JSON response format -type QueryActiveResponse struct { - Actives DenomList `json:"actives"` -} - -func (r QueryActiveResponse) String() (out string) { - out = r.Actives.String() - return strings.TrimSpace(out) -} - -func queryActive(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - denoms := keeper.getActiveDenoms(ctx) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryActiveResponse{Actives: denoms}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - - return bz, nil -} - -// QueryVoteParams for query 'custom/oracle/votes' -type QueryVotesParams struct { - Voter sdk.ValAddress - Denom string -} - -// NewQueryVotesParams creates a new instance of QueryVotesParams -func NewQueryVotesParams(voter sdk.ValAddress, denom string) QueryVotesParams { - return QueryVotesParams{ - Voter: voter, - Denom: denom, - } -} - -// JSON response format -type QueryVotesResponse struct { - Votes PriceVotes `json:"votes"` -} - -func (r QueryVotesResponse) String() (out string) { - out = r.Votes.String() - return strings.TrimSpace(out) -} - -func queryVotes(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - var params QueryVotesParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) - } - - filteredVotes := PriceVotes{} - - // collects all votes without filter - prefix := prefixVote - handler := func(vote PriceVote) (stop bool) { - filteredVotes = append(filteredVotes, vote) - return false - } - - // applies filter - if len(params.Denom) != 0 && !params.Voter.Empty() { - prefix = keyVote(params.Denom, params.Voter) - } else if len(params.Denom) != 0 { - prefix = keyVote(params.Denom, sdk.ValAddress{}) - } else if !params.Voter.Empty() { - handler = func(vote PriceVote) (stop bool) { - - if vote.Voter.Equals(params.Voter) { - filteredVotes = append(filteredVotes, vote) - } - - return false - } - } - - keeper.iterateVotesWithPrefix(ctx, prefix, handler) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryVotesResponse{Votes: filteredVotes}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// QueryPrevotesParams for query 'custom/oracle/prevotes' -type QueryPrevotesParams QueryVotesParams - -// NewQueryPrevotesParams creates a new instance of QueryPrevotesParams -func NewQueryPrevotesParams(voter sdk.ValAddress, denom string) QueryPrevotesParams { - return QueryPrevotesParams{ - Voter: voter, - Denom: denom, - } -} - -// JSON response format -type QueryPrevotesResponse struct { - Prevotes PricePrevotes `json:"prevotes"` -} - -func (r QueryPrevotesResponse) String() (out string) { - out = r.Prevotes.String() - return strings.TrimSpace(out) -} - -func queryPrevotes(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - var params QueryPrevotesParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) - } - - filteredPrevotes := PricePrevotes{} - - // collects all votes without filter - prefix := prefixPrevote - handler := func(prevote PricePrevote) (stop bool) { - filteredPrevotes = append(filteredPrevotes, prevote) - return false - } - - // applies filter - if len(params.Denom) != 0 && !params.Voter.Empty() { - prefix = keyPrevote(params.Denom, params.Voter) - } else if len(params.Denom) != 0 { - prefix = keyPrevote(params.Denom, sdk.ValAddress{}) - } else if !params.Voter.Empty() { - handler = func(prevote PricePrevote) (stop bool) { - - if prevote.Voter.Equals(params.Voter) { - filteredPrevotes = append(filteredPrevotes, prevote) - } - - return false - } - } - - keeper.iteratePrevotesWithPrefix(ctx, prefix, handler) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryPrevotesResponse{Prevotes: filteredPrevotes}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -func queryParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// QueryFeederDelegationParams for query 'custom/oracle/feeder-delegation' -type QueryFeederDelegationParams struct { - Validator sdk.ValAddress -} - -// NewQueryFeederDelegationParams creates a new instance of QueryFeederDelegationParams -func NewQueryFeederDelegationParams(validator sdk.ValAddress) QueryFeederDelegationParams { - return QueryFeederDelegationParams{ - Validator: validator, - } -} - -// JSON response format -type QueryFeederDelegationResponse struct { - Delegatee sdk.AccAddress `json:"delegatee"` -} - -func (r QueryFeederDelegationResponse) String() (out string) { - out = r.Delegatee.String() - return strings.TrimSpace(out) -} - -func queryFeederDelegation(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - var params QueryFeederDelegationParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) - } - - delegatee := keeper.GetFeedDelegate(ctx, params.Validator) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryFeederDelegationResponse{Delegatee: delegatee}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} diff --git a/x/oracle/querier_test.go b/x/oracle/querier_test.go deleted file mode 100644 index ecbdb55b6..000000000 --- a/x/oracle/querier_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package oracle - -import ( - "encoding/hex" - "strings" - "testing" - - "github.com/terra-project/core/types/assets" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const custom = "custom" - -func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) Params { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryParams}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryParams}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var params Params - err2 := cdc.UnmarshalJSON(bz, ¶ms) - require.Nil(t, err2) - - return params -} - -func getQueriedPrice(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, denom string) sdk.Dec { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryPrice}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryPrice, denom}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryPriceResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - return response.Price -} - -func getQueriedActive(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) DenomList { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryActive}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryActive}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryActiveResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - return response.Actives -} - -func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, voter sdk.ValAddress, denom string) PriceVotes { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryVotes}, "/"), - Data: cdc.MustMarshalJSON(NewQueryVotesParams(voter, denom)), - } - - bz, err := querier(ctx, []string{QueryVotes}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryVotesResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - return response.Votes -} - -func getQueriedPrevotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, voter sdk.ValAddress, denom string) PricePrevotes { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryPrevotes}, "/"), - Data: cdc.MustMarshalJSON(NewQueryPrevotesParams(voter, denom)), - } - - bz, err := querier(ctx, []string{QueryPrevotes}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryPrevotesResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - return response.Prevotes -} - -func getQueriedFeederDelegation(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validator sdk.ValAddress) sdk.AccAddress { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryFeederDelegation}, "/"), - Data: cdc.MustMarshalJSON(NewQueryFeederDelegationParams(validator)), - } - - bz, err := querier(ctx, []string{QueryFeederDelegation}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryFeederDelegationResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - return response.Delegatee -} - -func TestQueryParams(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - defaultParams := DefaultParams() - input.oracleKeeper.SetParams(input.ctx, defaultParams) - - params := getQueriedParams(t, input.ctx, input.cdc, querier) - - require.Equal(t, defaultParams, params) -} - -func TestQueryPrice(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - testPrice := sdk.NewDecWithPrec(48842, 4) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, testPrice) - - price := getQueriedPrice(t, input.ctx, input.cdc, querier, assets.MicroKRWDenom) - - require.Equal(t, testPrice, price) -} - -func TestQueryActives(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - empty := getQueriedActive(t, input.ctx, input.cdc, querier) - require.Equal(t, 0, len(empty)) - - testPrice := sdk.NewDecWithPrec(48842, 4) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, testPrice) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroUSDDenom, testPrice) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, testPrice) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, testPrice) - - actives := getQueriedActive(t, input.ctx, input.cdc, querier) - - require.Equal(t, 4, len(actives)) -} - -func TestQueryVotes(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - testPrice := sdk.NewDecWithPrec(48842, 4) - - votes := PriceVotes{ - // first voter votes - NewPriceVote(testPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])), - NewPriceVote(testPrice, assets.MicroKRWDenom, sdk.ValAddress(addrs[0])), - NewPriceVote(testPrice, assets.MicroUSDDenom, sdk.ValAddress(addrs[0])), - - // Second voter votes - NewPriceVote(testPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[1])), - NewPriceVote(testPrice, assets.MicroKRWDenom, sdk.ValAddress(addrs[1])), - NewPriceVote(testPrice, assets.MicroGBPDenom, sdk.ValAddress(addrs[1])), - - // Third voter votes - NewPriceVote(testPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[2])), - NewPriceVote(testPrice, assets.MicroCNYDenom, sdk.ValAddress(addrs[2])), - NewPriceVote(testPrice, assets.MicroGBPDenom, sdk.ValAddress(addrs[2])), - } - - for _, vote := range votes { - input.oracleKeeper.addVote(input.ctx, vote) - } - - voterOneSDR := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.ValAddress(addrs[0]), assets.MicroSDRDenom) - require.Equal(t, 1, len(voterOneSDR)) - - voterOne := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.ValAddress(addrs[0]), "") - require.Equal(t, 3, len(voterOne)) - - assetKRW := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.ValAddress{}, assets.MicroKRWDenom) - require.Equal(t, 2, len(assetKRW)) - - noFilters := getQueriedVotes(t, input.ctx, input.cdc, querier, sdk.ValAddress{}, "") - require.Equal(t, 9, len(noFilters)) -} - -func TestQueryPrevotes(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - testPrice := sdk.NewDecWithPrec(48842, 4) - - hash, _ := VoteHash("abcd", testPrice, assets.MicroSDRDenom, sdk.ValAddress(addrs[0])) - hexHash := hex.EncodeToString(hash) - prevotes := PricePrevotes{ - // first voter votes - NewPricePrevote(hexHash, assets.MicroSDRDenom, sdk.ValAddress(addrs[0]), 1), - NewPricePrevote(hexHash, assets.MicroKRWDenom, sdk.ValAddress(addrs[0]), 1), - NewPricePrevote(hexHash, assets.MicroUSDDenom, sdk.ValAddress(addrs[0]), 1), - - // Second voter votes - NewPricePrevote(hexHash, assets.MicroSDRDenom, sdk.ValAddress(addrs[1]), 1), - NewPricePrevote(hexHash, assets.MicroKRWDenom, sdk.ValAddress(addrs[1]), 1), - NewPricePrevote(hexHash, assets.MicroGBPDenom, sdk.ValAddress(addrs[1]), 1), - - // Third voter votes - NewPricePrevote(hexHash, assets.MicroSDRDenom, sdk.ValAddress(addrs[2]), 1), - NewPricePrevote(hexHash, assets.MicroCNYDenom, sdk.ValAddress(addrs[2]), 1), - NewPricePrevote(hexHash, assets.MicroGBPDenom, sdk.ValAddress(addrs[2]), 1), - } - - for _, prevote := range prevotes { - input.oracleKeeper.addPrevote(input.ctx, prevote) - } - - voterOneSDR := getQueriedPrevotes(t, input.ctx, input.cdc, querier, sdk.ValAddress(addrs[0]), assets.MicroSDRDenom) - require.Equal(t, 1, len(voterOneSDR)) - - voterOne := getQueriedPrevotes(t, input.ctx, input.cdc, querier, sdk.ValAddress(addrs[0]), "") - require.Equal(t, 3, len(voterOne)) - - assetKRW := getQueriedPrevotes(t, input.ctx, input.cdc, querier, sdk.ValAddress{}, assets.MicroKRWDenom) - require.Equal(t, 2, len(assetKRW)) - - noFilters := getQueriedPrevotes(t, input.ctx, input.cdc, querier, sdk.ValAddress{}, "") - require.Equal(t, 9, len(noFilters)) -} - -func TestQueryFeederDelegations(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.oracleKeeper) - - input.oracleKeeper.SetFeedDelegate(input.ctx, sdk.ValAddress(addrs[0]), addrs[1]) - - delegate := getQueriedFeederDelegation(t, input.ctx, input.cdc, querier, sdk.ValAddress(addrs[0])) - - require.Equal(t, sdk.AccAddress(sdk.ValAddress(addrs[1])), delegate) - require.Equal(t, sdk.AccAddress(sdk.ValAddress(addrs[2])), addrs[2]) - require.NotEqual(t, sdk.AccAddress(sdk.ValAddress(addrs[2])), addrs[1]) -} diff --git a/x/oracle/simulation/msgs.go b/x/oracle/simulation/msgs.go new file mode 100644 index 000000000..eb3fd6ebb --- /dev/null +++ b/x/oracle/simulation/msgs.go @@ -0,0 +1,83 @@ +package simulation + +import ( + "encoding/hex" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/simulation" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle" +) + +// SimulateMsgPrevote generates a MsgPrevote with random values +func SimulateMsgPrevote(k oracle.Keeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + acc := simulation.RandomAcc(r, accs) + valAddr := sdk.ValAddress(acc.Address) + bz, _ := oracle.VoteHash("1234", sdk.NewDec(1700), core.MicroSDRDenom, valAddr) + voteHash := hex.EncodeToString(bz) + + msg := oracle.NewMsgPricePrevote(voteHash, core.MicroSDRDenom, acc.Address, valAddr) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(oracle.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + ctx, write := ctx.CacheContext() + ok := oracle.NewHandler(k)(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgVote generates a MsgVote with random values +func SimulateMsgVote(k oracle.Keeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + acc := simulation.RandomAcc(r, accs) + valAddr := sdk.ValAddress(acc.Address) + + msg := oracle.NewMsgPriceVote(sdk.NewDec(1700), "1234", core.MicroSDRDenom, acc.Address, valAddr) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(oracle.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + ctx, write := ctx.CacheContext() + ok := oracle.NewHandler(k)(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgDelegateFeederPermission generates a MsgDelegateFeederPermission with random values +func SimulateMsgDelegateFeederPermission(k oracle.Keeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + acc := simulation.RandomAcc(r, accs) + acc2 := simulation.RandomAcc(r, accs) + valAddr := sdk.ValAddress(acc.Address) + msg := oracle.NewMsgDelegateFeederPermission(valAddr, acc2.Address) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(oracle.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + ok := oracle.NewHandler(k)(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} diff --git a/x/oracle/tags/tags.go b/x/oracle/tags/tags.go deleted file mode 100644 index 6dd736852..000000000 --- a/x/oracle/tags/tags.go +++ /dev/null @@ -1,20 +0,0 @@ -package tags - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Oracle tags -var ( - ActionPriceUpdate = "price-update" // normal cases - ActionTallyDropped = "tally-dropped" // emitted when price update is illiquid - - Action = sdk.TagAction - Denom = "denom" - Voter = "voter" - Power = "power" - Price = "price" - - Operator = "operator" - FeedDelegate = "feed_delegate" -) diff --git a/x/oracle/tally.go b/x/oracle/tally.go new file mode 100644 index 000000000..5bad19bf8 --- /dev/null +++ b/x/oracle/tally.go @@ -0,0 +1,50 @@ +package oracle + +import ( + "sort" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/terra-project/core/x/oracle/internal/types" +) + +// Calculates the median and returns it. Sets the set of voters to be rewarded, i.e. voted within +// a reasonable spread from the weighted median to the store +func tally(ctx sdk.Context, pb types.PriceBallot, k Keeper) (weightedMedian sdk.Dec, ballotWinners types.ClaimPool, ballotLosers []sdk.ValAddress) { + if !sort.IsSorted(pb) { + sort.Sort(pb) + } + + weightedMedian = pb.WeightedMedian(ctx, k.StakingKeeper) + standardDeviation := pb.StandardDeviation(ctx, k.StakingKeeper) + rewardSpread := k.RewardBand(ctx).QuoInt64(2) + + if standardDeviation.GT(rewardSpread) { + rewardSpread = standardDeviation + } + + for _, vote := range pb { + if vote.Price.GTE(weightedMedian.Sub(rewardSpread)) && vote.Price.LTE(weightedMedian.Add(rewardSpread)) { + if validator := k.StakingKeeper.Validator(ctx, vote.Voter); validator != nil { + power := validator.GetConsensusPower() + + ballotWinners = append(ballotWinners, types.Claim{ + Recipient: vote.Voter, + Weight: power, + }) + } else { + ballotLosers = append(ballotLosers, vote.Voter) + } + } + } + + return +} + +// ballot for the asset is passing the threshold amount of voting power +func ballotIsPassing(ctx sdk.Context, ballot types.PriceBallot, k Keeper) bool { + totalBondedPower := sdk.TokensToConsensusPower(k.StakingKeeper.TotalBondedTokens(ctx)) + voteThreshold := k.VoteThreshold(ctx) + thresholdVotes := voteThreshold.MulInt64(totalBondedPower).RoundInt() + ballotPower := sdk.NewInt(ballot.Power(ctx, k.StakingKeeper)) + return ballotPower.GTE(thresholdVotes) +} diff --git a/x/oracle/test_common.go b/x/oracle/test_common.go deleted file mode 100644 index c1d8876a1..000000000 --- a/x/oracle/test_common.go +++ /dev/null @@ -1,196 +0,0 @@ -package oracle - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/mint" - - "time" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - pubKeys = []crypto.PubKey{ - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - } - - addrs = []sdk.AccAddress{ - sdk.AccAddress(pubKeys[0].Address()), - sdk.AccAddress(pubKeys[1].Address()), - sdk.AccAddress(pubKeys[2].Address()), - } - - valConsPubKeys = []crypto.PubKey{ - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - } - - valConsAddrs = []sdk.ConsAddress{ - sdk.ConsAddress(valConsPubKeys[0].Address()), - sdk.ConsAddress(valConsPubKeys[1].Address()), - sdk.ConsAddress(valConsPubKeys[2].Address()), - } - - uSDRAmt = sdk.NewInt(1005 * assets.MicroUnit) - uLunaAmt = sdk.NewInt(10 * assets.MicroUnit) - - randomPrice = sdk.NewDecWithPrec(1049, 2) // swap rate - anotherRandomPrice = sdk.NewDecWithPrec(4882, 2) // swap rate - - oracleDecPrecision = 8 -) - -func setup(t *testing.T) (testInput, sdk.Handler) { - input := createTestInput(t) - h := NewHandler(input.oracleKeeper) - - defaultOracleParams := DefaultParams() - defaultOracleParams.VotePeriod = int64(1) // Set to one block for convinience - input.oracleKeeper.SetParams(input.ctx, defaultOracleParams) - - return input, h -} - -type testInput struct { - ctx sdk.Context - cdc *codec.Codec - accKeeper auth.AccountKeeper - bankKeeper bank.Keeper - oracleKeeper Keeper - stakingKeeper staking.Keeper - distrKeeper distr.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyOracle := sdk.NewKVStoreKey(StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingParams := staking.DefaultParams() - stakingParams.BondDenom = assets.MicroLunaDenom - stakingKeeper.SetParams(ctx, stakingParams) - - sh := staking.NewHandler(stakingKeeper) - for i, addr := range addrs { - err2 := mintKeeper.Mint(ctx, addr, sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt.MulRaw(3))) - require.NoError(t, err2) - - // Add validators - commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(sdk.ValAddress(addr), valConsPubKeys[i], - sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt), staking.Description{}, commission, sdk.OneInt()) - res := sh(ctx, msg) - require.True(t, res.IsOK()) - - distrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(addr)) - staking.EndBlocker(ctx, stakingKeeper) - } - - oracleKeeper := NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(DefaultParamspace), - ) - - return testInput{ctx, cdc, accKeeper, bankKeeper, oracleKeeper, stakingKeeper, distrKeeper} -} diff --git a/x/oracle/test_utils.go b/x/oracle/test_utils.go new file mode 100644 index 000000000..6c3e9d737 --- /dev/null +++ b/x/oracle/test_utils.go @@ -0,0 +1,211 @@ +// nolint:deadcode unused DONTCOVER +package oracle + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/oracle/internal/keeper" + "github.com/terra-project/core/x/oracle/internal/types" +) + +var ( + valTokens = sdk.TokensFromConsensusPower(42) + initTokens = sdk.TokensFromConsensusPower(100000) + valCoins = sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, valTokens)) + initCoins = sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, initTokens)) +) + +type testInput struct { + mApp *mock.App + oracleKeeper Keeper + stakingKeeper staking.Keeper + supplyKeeper supply.Keeper + distrKeeper distr.Keeper + addrs []sdk.AccAddress + pubKeys []crypto.PubKey + privKeys []crypto.PrivKey +} + +func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []auth.Account) testInput { + mApp := mock.NewApp() + + staking.RegisterCodec(mApp.Cdc) + RegisterCodec(mApp.Cdc) + supply.RegisterCodec(mApp.Cdc) + + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) + keyDistr := sdk.NewKVStoreKey(distr.StoreKey) + keyOracle := sdk.NewKVStoreKey(StoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + + blackListAddrs := map[string]bool{ + auth.FeeCollectorName: true, + staking.NotBondedPoolName: true, + staking.BondedPoolName: true, + distr.ModuleName: true, + types.ModuleName: true, + } + + pk := mApp.ParamsKeeper + + bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blackListAddrs) + + maccPerms := map[string][]string{ + distr.ModuleName: nil, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + types.ModuleName: nil, + } + + supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, maccPerms) + stakingKeeper := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) + distrKeeper := distr.NewKeeper(mApp.Cdc, keyDistr, pk.Subspace(distr.DefaultParamspace), stakingKeeper, supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, blackListAddrs) + + keeper := NewKeeper(mApp.Cdc, keyOracle, pk.Subspace(DefaultParamspace), distrKeeper, stakingKeeper, supplyKeeper, distr.ModuleName, DefaultCodespace) + + mApp.Router().AddRoute(RouterKey, NewHandler(keeper)) + mApp.QueryRouter().AddRoute(QuerierRoute, NewQuerier(keeper)) + + mApp.SetBeginBlocker(getBeginBloker(distrKeeper)) + mApp.SetEndBlocker(getEndBlocker(keeper, stakingKeeper)) + mApp.SetInitChainer(getInitChainer(mApp, keeper, stakingKeeper, supplyKeeper, genAccs, genState)) + + require.NoError(t, mApp.CompleteSetup(keyStaking, tKeyStaking, keyOracle, keySupply, keyDistr)) + + var ( + addrs []sdk.AccAddress + pubKeys []crypto.PubKey + privKeys []crypto.PrivKey + ) + + if genAccs == nil || len(genAccs) == 0 { + genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs, valCoins) + } + + mock.SetGenesis(mApp, genAccs) + + return testInput{mApp, keeper, stakingKeeper, supplyKeeper, distrKeeper, addrs, pubKeys, privKeys} +} + +func getBeginBloker(distrKeeper distr.Keeper) sdk.BeginBlocker { + return func(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + distr.BeginBlocker(ctx, req, distrKeeper) + + return abci.ResponseBeginBlock{ + Events: ctx.EventManager().ABCIEvents(), + } + } +} + +// oracle and staking endblocker +func getEndBlocker(keeper Keeper, sk staking.Keeper) sdk.EndBlocker { + return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, sk) + return abci.ResponseEndBlock{} + } +} + +// gov and staking initchainer +func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper, supplyKeeper supply.Keeper, accs []auth.Account, genState GenesisState) sdk.InitChainer { + return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + mapp.InitChainer(ctx, req) + + stakingGenesis := staking.DefaultGenesisState() + + totalSupply := sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, initTokens.MulRaw(int64(len(mapp.GenesisAccounts))))) + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + // set module accounts + govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner) + notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) + + supplyKeeper.SetModuleAccount(ctx, govAcc) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + supplyKeeper.SetModuleAccount(ctx, bondPool) + + validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis) + if genState.IsEmpty() { + InitGenesis(ctx, keeper, DefaultGenesisState()) + } else { + InitGenesis(ctx, keeper, genState) + } + + return abci.ResponseInitChain{ + Validators: validators, + } + } +} + +var ( + pubkeys = []crypto.PubKey{ + ed25519.GenPrivKey().PubKey(), + ed25519.GenPrivKey().PubKey(), + ed25519.GenPrivKey().PubKey(), + } + + testDescription = staking.NewDescription("T", "E", "S", "T") + testCommissionRates = staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) +) + +func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { + require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") + + for i := 0; i < len(addrs); i++ { + + valTokens := sdk.TokensFromConsensusPower(powerAmt[i]) + valCreateMsg := staking.NewMsgCreateValidator( + addrs[i], pubkeys[i], sdk.NewCoin(core.MicroLunaDenom, valTokens), + testDescription, testCommissionRates, sdk.OneInt(), + ) + + res := stakingHandler(ctx, valCreateMsg) + require.True(t, res.IsOK()) + } +} + +var ( + uSDRAmt = sdk.NewInt(1005 * core.MicroUnit) + stakingAmt = sdk.TokensFromConsensusPower(10) + + randomPrice = sdk.NewDec(1700) + anotherRandomPrice = sdk.NewDecWithPrec(4882, 2) // swap rate +) + +func setup(t *testing.T) (keeper.TestInput, sdk.Handler) { + input := keeper.CreateTestInput(t) + params := input.OracleKeeper.GetParams(input.Ctx) + params.VotePeriod = 1 + input.OracleKeeper.SetParams(input.Ctx, params) + h := NewHandler(input.OracleKeeper) + + sh := staking.NewHandler(input.StakingKeeper) + + // Validator created + got := sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[0], keeper.PubKeys[0], stakingAmt)) + require.True(t, got.IsOK()) + got = sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[1], keeper.PubKeys[1], stakingAmt)) + require.True(t, got.IsOK()) + got = sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[2], keeper.PubKeys[2], stakingAmt)) + require.True(t, got.IsOK()) + staking.EndBlocker(input.Ctx, input.StakingKeeper) + + return input, h +} diff --git a/x/oracle/types.go b/x/oracle/types.go deleted file mode 100644 index 25abf58d5..000000000 --- a/x/oracle/types.go +++ /dev/null @@ -1,18 +0,0 @@ -package oracle - -const ( - // ModuleName is the name of the oracle module - ModuleName = "oracle" - - // StoreKey is the string store representation - StoreKey = ModuleName - - // RouterKey is the msg router key for the oracle module - RouterKey = ModuleName - - // QuerierRoute is the query router key for the oracle module - QuerierRoute = ModuleName - - // DefaultParamspace is the paramspace notation - DefaultParamspace = ModuleName -) diff --git a/x/params/alias.go b/x/params/alias.go new file mode 100644 index 000000000..5501adbb9 --- /dev/null +++ b/x/params/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/params/internal/types/ +package params + +import ( + "github.com/terra-project/core/x/params/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/params/cosmos_alias.go b/x/params/cosmos_alias.go new file mode 100644 index 000000000..b883b9fc0 --- /dev/null +++ b/x/params/cosmos_alias.go @@ -0,0 +1,55 @@ +// nolint +package params + +import ( + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/params/types" +) + +const ( + StoreKey = params.StoreKey + TStoreKey = params.TStoreKey + TestParamStore = params.TestParamStore + DefaultCodespace = params.DefaultCodespace + CodeUnknownSubspace = params.CodeUnknownSubspace + CodeSettingParameter = params.CodeSettingParameter + CodeEmptyData = params.CodeEmptyData + ModuleName = params.ModuleName + RouterKey = params.RouterKey + ProposalTypeChange = params.ProposalTypeChange +) + +var ( + // functions aliases + NewSubspace = params.NewSubspace + NewKeyTable = params.NewKeyTable + DefaultTestComponents = params.DefaultTestComponents + ErrUnknownSubspace = params.ErrUnknownSubspace + ErrSettingParameter = params.ErrSettingParameter + ErrEmptyChanges = params.ErrEmptyChanges + ErrEmptySubspace = params.ErrEmptySubspace + ErrEmptyKey = params.ErrEmptyKey + ErrEmptyValue = params.ErrEmptyValue + NewParameterChangeProposal = params.NewParameterChangeProposal + NewParamChange = params.NewParamChange + NewParamChangeWithSubkey = params.NewParamChangeWithSubkey + ValidateChanges = params.ValidateChanges + NewKeeper = params.NewKeeper + NewParamChangeProposalHandler = params.NewParamChangeProposalHandler + + // variables aliases + CosmosModuleCdc = types.ModuleCdc +) + +type ( + ParamSetPair = params.ParamSetPair + ParamSetPairs = params.ParamSetPairs + ParamSet = params.ParamSet + Subspace = params.Subspace + ReadOnlySubspace = params.ReadOnlySubspace + KeyTable = params.KeyTable + ParameterChangeProposal = params.ParameterChangeProposal + ParamChange = params.ParamChange + Keeper = params.Keeper + CosmosAppModuleBasic = params.AppModuleBasic +) diff --git a/x/params/internal/types/codec.go b/x/params/internal/types/codec.go new file mode 100644 index 000000000..aa1c4a598 --- /dev/null +++ b/x/params/internal/types/codec.go @@ -0,0 +1,18 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// RegisterCodec registers all necessary param module types with a given codec. +func RegisterCodec(cdc *codec.Codec) { +} + +// module codec +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/params/module.go b/x/params/module.go new file mode 100644 index 000000000..89344b9b9 --- /dev/null +++ b/x/params/module.go @@ -0,0 +1,53 @@ +package params + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} diff --git a/x/pay/client/cli/cli_test.go b/x/pay/client/cli/cli_test.go deleted file mode 100644 index 54e387002..000000000 --- a/x/pay/client/cli/cli_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" -) - -func TestSendTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - txCmd.AddCommand(SendTxCmd(cdc)) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `send`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--coins=1000uluna`, - `--to=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} diff --git a/x/pay/client/cli/sendtx.go b/x/pay/client/cli/sendtx.go deleted file mode 100644 index 731ebe9a7..000000000 --- a/x/pay/client/cli/sendtx.go +++ /dev/null @@ -1,97 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/bank" - - "github.com/spf13/cobra" -) - -const ( - flagTo = "to" - flagCoins = "coins" - flagOffline = "offline" -) - -// SendTxCmd will create a send tx and sign it with the given key. -func SendTxCmd(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "send --to [to_address] --coins [amount] --from [from_address or key_name]", - Args: cobra.NoArgs, - Short: "Create and sign a send tx", - Long: strings.TrimSpace(` -Create, sign and broadcast send tx. - -For generate-only, --from should be specified as address not key name. -$ terracli tx send --to [to_address] --coins [amount] --from [from_address or key_name] -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - toStr := viper.GetString(flagTo) - - to, err := sdk.AccAddressFromBech32(toStr) - if err != nil { - return err - } - - coinsStr := viper.GetString(flagCoins) - - // parse coins trying to be sent - coins, err := sdk.ParseCoins(coinsStr) - if err != nil { - return err - } - - from := cliCtx.GetFromAddress() - - offline := viper.GetBool(flagOffline) - if !offline { - - if err := cliCtx.EnsureAccountExists(); err != nil { - return err - } - - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - - // ensure account has enough coins - if !account.GetCoins().IsAllGTE(coins) { - return fmt.Errorf("address %s doesn't have enough coins to pay for this transaction", from) - } - - } - - // build and sign the transaction, then broadcast to Tendermint - msg := bank.NewMsgSend(from, to, coins) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd = client.PostCommands(cmd)[0] - - cmd.Flags().String(flagTo, "", "Destination address for sending") - cmd.Flags().String(flagCoins, "", "Amount of coins (e.g. 1000uluna,100usdr)") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection the node can still build and sign tx") - - cmd.MarkFlagRequired(client.FlagFrom) - cmd.MarkFlagRequired(flagTo) - cmd.MarkFlagRequired(flagCoins) - - return cmd -} diff --git a/x/pay/client/rest/sendtx.go b/x/pay/client/rest/sendtx.go deleted file mode 100644 index 9312d2f15..000000000 --- a/x/pay/client/rest/sendtx.go +++ /dev/null @@ -1,71 +0,0 @@ -package rest - -import ( - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/bank" -) - -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST") -} - -// SendReq defines the properties of a send request's body. -type SendReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Coins sdk.Coins `json:"coins"` -} - -// SendRequestHandlerFn - http request handler to send coins to a address. -func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32Addr := vars["address"] - - toAddr, err := sdk.AccAddressFromBech32(bech32Addr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - var req SendReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - account, err := cliCtx.GetAccount(fromAddr.Bytes()) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - req.BaseReq.AccountNumber = account.GetAccountNumber() - - if req.BaseReq.Sequence == 0 { - req.BaseReq.Sequence = account.GetSequence() - } - - msg := bank.NewMsgSend(fromAddr, toAddr, req.Coins) - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} diff --git a/x/pay/codec.go b/x/pay/codec.go deleted file mode 100644 index 954558975..000000000 --- a/x/pay/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -// Pay TODO - mandatory update - -package pay - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/bank" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(bank.MsgSend{}, "pay/MsgSend", nil) - cdc.RegisterConcrete(bank.MsgMultiSend{}, "pay/MsgMultiSend", nil) -} - -var msgCdc = codec.New() - -func init() { - RegisterCodec(msgCdc) -} diff --git a/x/pay/handler.go b/x/pay/handler.go deleted file mode 100644 index 0beb69058..000000000 --- a/x/pay/handler.go +++ /dev/null @@ -1,131 +0,0 @@ -// Package pay contains a forked version of the bank module. It only contains -// a modified message handler to support the payement of stability taxes. -// -// Taxes are of the fomula: min(principal * taxRate, taxCap). -// TaxCap and taxRate are stored by the treasury module. -// Should transactions fail midway, taxes are still paid and non-refundable. -package pay - -import ( - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/treasury" - - "github.com/cosmos/cosmos-sdk/x/auth" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank" -) - -// NewHandler returns a handler for "bank" type messages. -func NewHandler(k bank.Keeper, tk treasury.Keeper, fk auth.FeeCollectionKeeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case bank.MsgSend: - return handleMsgSend(ctx, k, tk, fk, msg) - - case bank.MsgMultiSend: - return handleMsgMultiSend(ctx, k, tk, fk, msg) - - default: - errMsg := "Unrecognized bank Msg type: %s" + msg.Type() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// Handle MsgPay. -func handleMsgSend(ctx sdk.Context, k bank.Keeper, tk treasury.Keeper, fk auth.FeeCollectionKeeper, msg bank.MsgSend) sdk.Result { - if !k.GetSendEnabled(ctx) { - return bank.ErrSendDisabled(k.Codespace()).Result() - } - - taxes, err := payTax(ctx, k, tk, fk, msg.FromAddress, msg.Amount) - if err != nil { - return err.Result() - } - - log := NewLog() - log = log.append(LogKeyTax, taxes.String()) - - resultTags := sdk.NewTags() - sendTags, err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount) - if err != nil { - return err.Result() - } - - resultTags = resultTags.AppendTags(sendTags) - - return sdk.Result{ - Tags: resultTags, - Log: log.String(), - } -} - -// Handle MsgMultiSend. -func handleMsgMultiSend(ctx sdk.Context, k bank.Keeper, tk treasury.Keeper, fk auth.FeeCollectionKeeper, msg bank.MsgMultiSend) sdk.Result { - // NOTE: totalIn == totalOut should already have been checked - if !k.GetSendEnabled(ctx) { - return bank.ErrSendDisabled(k.Codespace()).Result() - } - - totalTaxes := sdk.Coins{} - for _, input := range msg.Inputs { - taxes, taxErr := payTax(ctx, k, tk, fk, input.Address, input.Coins) - if taxErr != nil { - return taxErr.Result() - } - - totalTaxes = totalTaxes.Add(taxes).Sort() - } - - log := NewLog() - log = log.append(LogKeyTax, totalTaxes.String()) - - resultTags := sdk.NewTags() - sendTags, sendErr := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) - if sendErr != nil { - return sendErr.Result() - } - - resultTags = resultTags.AppendTags(sendTags) - return sdk.Result{ - Tags: resultTags, - Log: log.String(), - } -} - -// payTax charges the stability tax on MsgSend and MsgMultiSend. -func payTax(ctx sdk.Context, bk bank.Keeper, tk treasury.Keeper, fk auth.FeeCollectionKeeper, - taxPayer sdk.AccAddress, principal sdk.Coins) (taxes sdk.Coins, err sdk.Error) { - - taxRate := tk.GetTaxRate(ctx, util.GetEpoch(ctx)) - - if taxRate.Equal(sdk.ZeroDec()) { - return nil, nil - } - - for _, coin := range principal { - taxDue := sdk.NewDecFromInt(coin.Amount).Mul(taxRate).TruncateInt() - - // If tax due is greater than the tax cap, cap! - taxCap := tk.GetTaxCap(ctx, coin.Denom) - if taxDue.GT(taxCap) { - taxDue = taxCap - } - - if taxDue.Equal(sdk.ZeroInt()) { - continue - } - - taxes = taxes.Add(sdk.NewCoins(sdk.NewCoin(coin.Denom, taxDue))).Sort() - } - - _, _, err = bk.SubtractCoins(ctx, taxPayer, taxes) - if err != nil { - return nil, err - } - - fk.AddCollectedFees(ctx, taxes) - tk.RecordTaxProceeds(ctx, taxes) - return -} diff --git a/x/pay/handler_test.go b/x/pay/handler_test.go deleted file mode 100644 index c5545cced..000000000 --- a/x/pay/handler_test.go +++ /dev/null @@ -1,324 +0,0 @@ -package pay - -import ( - "fmt" - "testing" - "time" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - "github.com/terra-project/core/x/treasury" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - addrs = []sdk.AccAddress{ - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()), - } - - valConsPubKeys = []crypto.PubKey{ - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - } - - valConsAddrs = []sdk.ConsAddress{ - sdk.ConsAddress(valConsPubKeys[0].Address()), - sdk.ConsAddress(valConsPubKeys[1].Address()), - sdk.ConsAddress(valConsPubKeys[2].Address()), - } - - uSDRAmount = sdk.NewInt(1005).MulRaw(assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - accKeeper auth.AccountKeeper - bankKeeper bank.Keeper - treasuryKeeper treasury.Keeper - feeKeeper auth.FeeCollectionKeeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - bank.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyTreasury := sdk.NewKVStoreKey(treasury.StoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - keyMarket := sdk.NewKVStoreKey(market.StoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyTreasury, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingKeeper.SetParams(ctx, staking.DefaultParams()) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - &stakingKeeper, - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := market.NewKeeper(cdc, keyMarket, oracleKeeper, mintKeeper, - paramsKeeper.Subspace(market.DefaultParamspace)) - marketKeeper.SetParams(ctx, market.DefaultParams()) - - treasuryKeeper := treasury.NewKeeper( - cdc, - keyTreasury, - stakingKeeper.GetValidatorSet(), - mintKeeper, - marketKeeper, - paramsKeeper.Subspace(treasury.DefaultParamspace), - ) - - for _, addr := range addrs { - _, _, err := bankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, uSDRAmount)}) - require.NoError(t, err) - } - - return testInput{ctx, accKeeper, bankKeeper, treasuryKeeper, feeCollectionKeeper} -} - -func TestHandlerMsgSendTransfersDisabled(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, false) - - handler := NewHandler(input.bankKeeper, input.treasuryKeeper, input.feeKeeper) - amt := sdk.NewInt(5) - msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, amt)}) - - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, uSDRAmount)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, uSDRAmount)}) -} - -func TestHandlerMsgSendTransfersEnabled(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, true) - - params := treasury.DefaultParams() - input.treasuryKeeper.SetParams(input.ctx, params) - input.treasuryKeeper.SetTaxRate(input.ctx, sdk.ZeroDec()) // 0.0% - - handler := NewHandler(input.bankKeeper, input.treasuryKeeper, input.feeKeeper) - amt := sdk.NewInt(5).MulRaw(assets.MicroUnit) - msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, amt)}) - - res := handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - from := input.accKeeper.GetAccount(input.ctx, addrs[0]) - balance := uSDRAmount.Sub(amt) - require.Equal(t, from.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, balance)}) - - to := input.accKeeper.GetAccount(input.ctx, addrs[1]) - balance = uSDRAmount.Add(amt) - require.Equal(t, to.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, balance)}) -} - -func TestHandlerMsgSendTax(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, true) - params := treasury.DefaultParams() - - input.treasuryKeeper.SetTaxRate(input.ctx, sdk.NewDecWithPrec(1, 3)) // 0.1% - input.treasuryKeeper.SetParams(input.ctx, params) - - handler := NewHandler(input.bankKeeper, input.treasuryKeeper, input.feeKeeper) - amt := sdk.NewInt(1000).MulRaw(assets.MicroUnit) - msg := bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, amt)}) - - handler(input.ctx, msg) - - taxCollected := input.feeKeeper.GetCollectedFees(input.ctx) - taxRecorded := input.treasuryKeeper.PeekTaxProceeds(input.ctx, util.GetEpoch(input.ctx)) - require.Equal(t, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1).MulRaw(assets.MicroUnit))}, taxCollected) - require.Equal(t, taxCollected, taxRecorded) - - remainingBalance := input.bankKeeper.GetCoins(input.ctx, addrs[0]) - require.Equal(t, remainingBalance, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(4).MulRaw(assets.MicroUnit))}, "expected 4 SDR to be remaining") - - amt = sdk.NewInt(5).MulRaw(assets.MicroUnit) - msg = bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, amt)}) - res := handler(input.ctx, msg) - require.False(t, res.IsOK(), "expected failed message execution: %v", res.Log) - - // Clear coin balances - err := input.bankKeeper.SetCoins(input.ctx, addrs[0], sdk.Coins{}) - require.Nil(t, err) - err = input.bankKeeper.SetCoins(input.ctx, addrs[1], sdk.Coins{}) - require.Nil(t, err) - - // Give more coins - _, _, err = input.bankKeeper.AddCoins(input.ctx, addrs[0], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(5000).MulRaw(assets.MicroUnit))}) - require.Nil(t, err) - - // Reset tax cap - params.TaxPolicy.Cap = sdk.NewInt64Coin(assets.MicroSDRDenom, sdk.NewInt(2).MulRaw(assets.MicroUnit).Int64()) // 2 SDR cap - input.treasuryKeeper.SetParams(input.ctx, params) - amt = sdk.NewInt(2000).MulRaw(assets.MicroUnit) - msg = bank.NewMsgSend(addrs[0], addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, amt)}) - res = handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - remainingBalance = input.bankKeeper.GetCoins(input.ctx, addrs[0]) - expectedRemainingBalance := sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(2999).MulRaw(assets.MicroUnit))} - receivedBalance := input.bankKeeper.GetCoins(input.ctx, addrs[1]) - expectedReceivedBalance := sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(2000).MulRaw(assets.MicroUnit))} - taxCollected = input.feeKeeper.GetCollectedFees(input.ctx) - expectedTaxCollected := sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewDecFromIntWithPrec(sdk.NewInt(2005000), 6).MulInt64(assets.MicroUnit).TruncateInt())} - - fmt.Println(taxCollected) - require.Equal(t, expectedRemainingBalance, remainingBalance) - require.Equal(t, expectedReceivedBalance, receivedBalance) - require.Equal(t, expectedTaxCollected, taxCollected) -} - -func TestHandlerMsgMultiSendTax(t *testing.T) { - input := createTestInput(t) - input.bankKeeper.SetSendEnabled(input.ctx, true) - - input.treasuryKeeper.SetTaxRate(input.ctx, sdk.NewDecWithPrec(1, 2)) // 1% - - params := treasury.DefaultParams() - params.TaxPolicy.Cap = sdk.NewInt64Coin(assets.MicroSDRDenom, sdk.NewInt(2).MulRaw(assets.MicroUnit).Int64()) // 2 SDR cap - input.treasuryKeeper.SetParams(input.ctx, params) - - handler := NewHandler(input.bankKeeper, input.treasuryKeeper, input.feeKeeper) - - msg := bank.NewMsgMultiSend( - []bank.Input{ - bank.NewInput(addrs[0], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(398).MulRaw(assets.MicroUnit))}), - bank.NewInput(addrs[1], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(14).MulRaw(assets.MicroUnit))}), - bank.NewInput(addrs[2], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(189).MulRaw(assets.MicroUnit))}), - }, - []bank.Output{ - bank.NewOutput(addrs[0], sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(601).MulRaw(assets.MicroUnit))}), - }, - ) - - res := handler(input.ctx, msg) - require.True(t, res.IsOK(), "expected successful message execution: %v", res.Log) - - taxCollected := input.feeKeeper.GetCollectedFees(input.ctx) - taxRecorded := input.treasuryKeeper.PeekTaxProceeds(input.ctx, util.GetEpoch(input.ctx)) - - require.Equal(t, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewDecFromIntWithPrec(sdk.NewInt(403), 2).MulInt64(assets.MicroUnit).TruncateInt())}, taxCollected) - require.Equal(t, taxCollected, taxRecorded) - - acc1 := input.accKeeper.GetAccount(input.ctx, addrs[0]) - balance := uSDRAmount.Add(sdk.NewInt(201).MulRaw(assets.MicroUnit)) - require.Equal(t, acc1.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, balance)}) - - acc2 := input.accKeeper.GetAccount(input.ctx, addrs[1]) - balance = uSDRAmount.Sub(sdk.NewDecFromIntWithPrec(sdk.NewInt(1414), 2).MulInt64(assets.MicroUnit).TruncateInt()) - require.Equal(t, acc2.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, balance)}) - - acc3 := input.accKeeper.GetAccount(input.ctx, addrs[2]) - balance = uSDRAmount.Sub(sdk.NewDecFromIntWithPrec(sdk.NewInt(19089), 2).MulInt64(assets.MicroUnit).TruncateInt()) - require.Equal(t, acc3.GetCoins(), sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, balance)}) -} diff --git a/x/pay/log.go b/x/pay/log.go deleted file mode 100644 index 286b72c38..000000000 --- a/x/pay/log.go +++ /dev/null @@ -1,28 +0,0 @@ -package pay - -import ( - "encoding/json" -) - -const ( - // LogKeyTax is to record treasury tax for a pay msg - LogKeyTax = string("tax") -) - -// Log is map type object to organize msg result -type Log map[string]string - -func NewLog() Log { - return Log{} -} - -func (log Log) append(key, value string) Log { - log[key] = value - - return log -} - -func (log Log) String() string { - jsonMap, _ := json.Marshal(log) - return string(jsonMap) -} diff --git a/x/slashing/alias.go b/x/slashing/alias.go new file mode 100644 index 000000000..93dc80394 --- /dev/null +++ b/x/slashing/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/slashing/internal/types/ +package slashing + +import ( + "github.com/terra-project/core/x/slashing/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/slashing/client/cli/flags.go b/x/slashing/client/cli/flags.go deleted file mode 100755 index 388b8ddce..000000000 --- a/x/slashing/client/cli/flags.go +++ /dev/null @@ -1,7 +0,0 @@ -package cli - -// nolint -const ( - FlagAddressValidator = "validator" - FlagConsensusPubKeyValidator = "validator-conspub" -) diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go deleted file mode 100755 index d35d45e54..000000000 --- a/x/slashing/client/cli/query.go +++ /dev/null @@ -1,84 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" // XXX fix - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" -) - -// GetCmdQuerySigningInfo implements the command to query signing info. -func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "signing-info --validator-conspub [validator-conspub]", - Short: "Query a validator's signing information", - Long: strings.TrimSpace(`Use a validators' consensus public key to find the signing-info for that validator: - -$ terracli query slashing signing-info --validator-conspub terravalconspub1zcjduepqs5s0vddx5m65h5ntjzwd0x8g3245rgrytpds4ds7vdtlwx06mcesmnkzly -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - consPubKey := viper.GetString(FlagConsensusPubKeyValidator) - pk, err := sdk.GetConsPubKeyBech32(consPubKey) - if err != nil { - return err - } - - consAddr := sdk.ConsAddress(pk.Address()) - key := slashing.GetValidatorSigningInfoKey(consAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - return err - } - - if len(res) == 0 { - return fmt.Errorf("Validator %s not found in slashing store", consAddr) - } - - var signingInfo slashing.ValidatorSigningInfo - cdc.MustUnmarshalBinaryLengthPrefixed(res, &signingInfo) - return cliCtx.PrintOutput(signingInfo) - }, - } - - cmd.Flags().String(FlagConsensusPubKeyValidator, "", "validators' consensus public key") - - cmd.MarkFlagRequired(FlagConsensusPubKeyValidator) - - return cmd -} - -// GetCmdQueryParams implements a command to fetch slashing parameters. -func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "params", - Short: "Query the current slashing parameters", - Args: cobra.NoArgs, - Long: strings.TrimSpace(`Query genesis parameters for the slashing module: - -$ terracli query slashing params -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/parameters", slashing.QuerierRoute) - res, err := cliCtx.QueryWithData(route, nil) - if err != nil { - return err - } - - var params slashing.Params - cdc.MustUnmarshalJSON(res, ¶ms) - return cliCtx.PrintOutput(params) - }, - } -} diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go deleted file mode 100755 index bcb16ae68..000000000 --- a/x/slashing/client/cli/tx.go +++ /dev/null @@ -1,36 +0,0 @@ -package cli - -import ( - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/slashing" - - "github.com/spf13/cobra" -) - -// GetCmdUnjail implements the create unjail validator command. -func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "unjail", - Args: cobra.NoArgs, - Short: "unjail validator previously jailed for downtime", - Long: `unjail a jailed validator: - -$ terracli tx slashing unjail --from mykey -`, - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - valAddr := cliCtx.GetFromAddress() - - msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr)) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false) - }, - } -} diff --git a/x/slashing/client/module_client.go b/x/slashing/client/module_client.go deleted file mode 100755 index b6138a93a..000000000 --- a/x/slashing/client/module_client.go +++ /dev/null @@ -1,53 +0,0 @@ -package client - -import ( - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/terra-project/core/x/slashing/client/cli" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - // Group slashing queries under a subcommand - slashingQueryCmd := &cobra.Command{ - Use: slashing.ModuleName, - Short: "Querying commands for the slashing module", - } - - slashingQueryCmd.AddCommand( - client.GetCommands( - cli.GetCmdQuerySigningInfo(mc.storeKey, mc.cdc), - cli.GetCmdQueryParams(mc.cdc), - )..., - ) - - return slashingQueryCmd - -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - slashingTxCmd := &cobra.Command{ - Use: slashing.ModuleName, - Short: "Slashing transactions subcommands", - } - - slashingTxCmd.AddCommand(client.PostCommands( - cli.GetCmdUnjail(mc.cdc), - )...) - - return slashingTxCmd -} diff --git a/x/slashing/client/module_client_test.go b/x/slashing/client/module_client_test.go deleted file mode 100644 index fd3e7ce89..000000000 --- a/x/slashing/client/module_client_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "params": true, - "signing-info": true, - } - - txCmdList = map[string]bool{ - "unjail": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go deleted file mode 100755 index 5da8ad825..000000000 --- a/x/slashing/client/rest/query.go +++ /dev/null @@ -1,163 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/slashing" -) - -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc( - "/slashing/validators/{validatorPubKey}/signing_info", - signingInfoHandlerFn(cliCtx, slashing.StoreKey, cdc), - ).Methods("GET") - - r.HandleFunc( - "/slashing/signing_infos", - signingInfoHandlerListFn(cliCtx, slashing.StoreKey, cdc), - ).Methods("GET").Queries("page", "{page}", "limit", "{limit}") - - r.HandleFunc( - "/slashing/parameters", - queryParamsHandlerFn(cdc, cliCtx), - ).Methods("GET") -} - -// http request handler to query signing info -func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - pk, err := sdk.GetConsPubKeyBech32(vars["validatorPubKey"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - signingInfo, code, err := getSigningInfo(cliCtx, storeName, cdc, pk.Address()) - - if err != nil { - rest.WriteErrorResponse(w, code, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent) - } -} - -// http request handler to query signing info -func signingInfoHandlerListFn(cliCtx context.CLIContext, storeName string, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var signingInfoList []slashing.ValidatorSigningInfo - - _, page, limit, err := rest.ParseHTTPArgs(r) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - height, err := rpc.GetChainHeight(cliCtx) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - validators, err := rpc.GetValidators(cliCtx, &height) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if len(validators.Validators) == 0 { - rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent) - return - } - - // TODO: this should happen when querying Validators from RPC, - // as soon as it's available this is not needed anymore - // parameter page is (page-1) because ParseHTTPArgs starts with page 1, where our array start with 0 - start, end := adjustPagination(uint(len(validators.Validators)), uint(page)-1, uint(limit)) - for _, validator := range validators.Validators[start:end] { - address := validator.Address - signingInfo, code, err := getSigningInfo(cliCtx, storeName, cdc, address) - if err != nil { - rest.WriteErrorResponse(w, code, err.Error()) - return - } - signingInfoList = append(signingInfoList, signingInfo) - } - - if len(signingInfoList) == 0 { - rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent) - return - } - - rest.PostProcessResponse(w, cdc, signingInfoList, cliCtx.Indent) - } -} - -func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - route := fmt.Sprintf("custom/%s/parameters", slashing.QuerierRoute) - - res, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func getSigningInfo(cliCtx context.CLIContext, storeName string, cdc *codec.Codec, address []byte) (signingInfo slashing.ValidatorSigningInfo, code int, err error) { - key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(address)) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - code = http.StatusInternalServerError - return - } - - if len(res) == 0 { - code = http.StatusOK - return - } - - err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo) - if err != nil { - code = http.StatusInternalServerError - return - } - - return -} - -// Adjust pagination with page starting from 0 -func adjustPagination(size, page, limit uint) (start uint, end uint) { - // If someone asks for pages bigger than our dataset, just return everything - if limit > size { - return 0, size - } - - // Do pagination when healthy, fallback to 0 - start = 0 - if page*limit < size { - start = page * limit - } - - // Do pagination only when healthy, fallback to len(dataset) - end = size - if start+limit <= size { - end = start + limit - } - - return start, end -} diff --git a/x/slashing/client/rest/query_test.go b/x/slashing/client/rest/query_test.go deleted file mode 100755 index ad177c23b..000000000 --- a/x/slashing/client/rest/query_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package rest - -import ( - "github.com/stretchr/testify/require" - "testing" -) - -func TestAdjustPagination(t *testing.T) { - type args struct { - s string - } - tests := []struct { - name string - size uint - page uint - limit uint - start uint - end uint - }{ - {"Ok", 3, 0, 1, 0, 1}, - {"Limit too big", 3, 1, 5, 0, 3}, - {"Page over limit", 3, 2, 3, 0, 3}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - start, end := adjustPagination(tt.size, tt.page, tt.limit) - require.Equal(t, tt.start, start) - require.Equal(t, tt.end, end) - }) - } -} diff --git a/x/slashing/client/rest/rest.go b/x/slashing/client/rest/rest.go deleted file mode 100755 index d06cd719d..000000000 --- a/x/slashing/client/rest/rest.go +++ /dev/null @@ -1,15 +0,0 @@ -package rest - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" -) - -// RegisterRoutes registers staking-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - registerQueryRoutes(cliCtx, r, cdc) - registerTxRoutes(cliCtx, r, cdc, kb) -} diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go deleted file mode 100755 index 95b93740b..000000000 --- a/x/slashing/client/rest/tx.go +++ /dev/null @@ -1,72 +0,0 @@ -package rest - -import ( - "bytes" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/slashing" -) - -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - r.HandleFunc( - "/slashing/validators/{validatorAddr}/unjail", - unjailRequestHandlerFn(cdc, kb, cliCtx), - ).Methods("POST") -} - -// Unjail TX body -type UnjailReq struct { - BaseReq rest.BaseReq `json:"base_req"` -} - -func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - - bech32validator := vars["validatorAddr"] - - var req UnjailReq - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - valAddr, err := sdk.ValAddressFromBech32(bech32validator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - if !bytes.Equal(fromAddr, valAddr) { - rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address") - return - } - - msg := slashing.NewMsgUnjail(valAddr) - err = msg.ValidateBasic() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} diff --git a/x/slashing/codec.go b/x/slashing/codec.go deleted file mode 100644 index 34eb3817d..000000000 --- a/x/slashing/codec.go +++ /dev/null @@ -1,13 +0,0 @@ -package slashing - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/slashing" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(slashing.MsgUnjail{}, "slashing/MsgUnjail", nil) -} - -var cdcEmpty = codec.New() diff --git a/x/slashing/cosmos_alias.go b/x/slashing/cosmos_alias.go new file mode 100644 index 000000000..e7f5363c7 --- /dev/null +++ b/x/slashing/cosmos_alias.go @@ -0,0 +1,85 @@ +// nolint +package slashing + +import ( + "github.com/cosmos/cosmos-sdk/x/slashing" +) + +const ( + DefaultCodespace = slashing.DefaultCodespace + CodeInvalidValidator = slashing.CodeInvalidValidator + CodeValidatorJailed = slashing.CodeValidatorJailed + CodeValidatorNotJailed = slashing.CodeValidatorNotJailed + CodeMissingSelfDelegation = slashing.CodeMissingSelfDelegation + CodeSelfDelegationTooLow = slashing.CodeSelfDelegationTooLow + CodeMissingSigningInfo = slashing.CodeMissingSigningInfo + ModuleName = slashing.ModuleName + StoreKey = slashing.StoreKey + RouterKey = slashing.RouterKey + QuerierRoute = slashing.QuerierRoute + QueryParameters = slashing.QueryParameters + QuerySigningInfo = slashing.QuerySigningInfo + QuerySigningInfos = slashing.QuerySigningInfos + DefaultParamspace = slashing.DefaultParamspace + DefaultMaxEvidenceAge = slashing.DefaultMaxEvidenceAge + DefaultSignedBlocksWindow = slashing.DefaultSignedBlocksWindow + DefaultDowntimeJailDuration = slashing.DefaultDowntimeJailDuration +) + +var ( + // functions aliases + ErrNoValidatorForAddress = slashing.ErrNoValidatorForAddress + ErrBadValidatorAddr = slashing.ErrBadValidatorAddr + ErrValidatorJailed = slashing.ErrValidatorJailed + ErrValidatorNotJailed = slashing.ErrValidatorNotJailed + ErrMissingSelfDelegation = slashing.ErrMissingSelfDelegation + ErrSelfDelegationTooLowToUnjail = slashing.ErrSelfDelegationTooLowToUnjail + ErrNoSigningInfoFound = slashing.ErrNoSigningInfoFound + NewGenesisState = slashing.NewGenesisState + DefaultGenesisState = slashing.DefaultGenesisState + ValidateGenesis = slashing.ValidateGenesis + GetValidatorSigningInfoKey = slashing.GetValidatorSigningInfoKey + GetValidatorSigningInfoAddress = slashing.GetValidatorSigningInfoAddress + GetValidatorMissedBlockBitArrayPrefixKey = slashing.GetValidatorMissedBlockBitArrayPrefixKey + GetValidatorMissedBlockBitArrayKey = slashing.GetValidatorMissedBlockBitArrayKey + GetAddrPubkeyRelationKey = slashing.GetAddrPubkeyRelationKey + NewMsgUnjail = slashing.NewMsgUnjail + ParamKeyTable = slashing.ParamKeyTable + NewParams = slashing.NewParams + DefaultParams = slashing.DefaultParams + NewQuerySigningInfoParams = slashing.NewQuerySigningInfoParams + NewQuerySigningInfosParams = slashing.NewQuerySigningInfosParams + NewValidatorSigningInfo = slashing.NewValidatorSigningInfo + NewCosmosAppModule = slashing.NewAppModule + NewKeeper = slashing.NewKeeper + + // variable aliases + CosmosModuleCdc = slashing.ModuleCdc + ValidatorSigningInfoKey = slashing.ValidatorSigningInfoKey + ValidatorMissedBlockBitArrayKey = slashing.ValidatorMissedBlockBitArrayKey + AddrPubkeyRelationKey = slashing.AddrPubkeyRelationKey + DoubleSignJailEndTime = slashing.DoubleSignJailEndTime + DefaultMinSignedPerWindow = slashing.DefaultMinSignedPerWindow + DefaultSlashFractionDoubleSign = slashing.DefaultSlashFractionDoubleSign + DefaultSlashFractionDowntime = slashing.DefaultSlashFractionDowntime + KeyMaxEvidenceAge = slashing.KeyMaxEvidenceAge + KeySignedBlocksWindow = slashing.KeySignedBlocksWindow + KeyMinSignedPerWindow = slashing.KeyMinSignedPerWindow + KeyDowntimeJailDuration = slashing.KeyDowntimeJailDuration + KeySlashFractionDoubleSign = slashing.KeySlashFractionDoubleSign + KeySlashFractionDowntime = slashing.KeySlashFractionDowntime +) + +type ( + CodeType = slashing.CodeType + GenesisState = slashing.GenesisState + MissedBlock = slashing.MissedBlock + MsgUnjail = slashing.MsgUnjail + Params = slashing.Params + QuerySigningInfoParams = slashing.QuerySigningInfoParams + QuerySigningInfosParams = slashing.QuerySigningInfosParams + ValidatorSigningInfo = slashing.ValidatorSigningInfo + Keeper = slashing.Keeper + CosmosAppModule = slashing.AppModule + CosmosAppModuleBasic = slashing.AppModuleBasic +) diff --git a/x/slashing/internal/types/codec.go b/x/slashing/internal/types/codec.go new file mode 100644 index 000000000..973f856cb --- /dev/null +++ b/x/slashing/internal/types/codec.go @@ -0,0 +1,21 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/slashing" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(slashing.MsgUnjail{}, "cosmos/MsgUnjail", nil) +} + +// module codec +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/slashing/internal/types/expected_keepers.go b/x/slashing/internal/types/expected_keepers.go new file mode 100644 index 000000000..fd5fd2d76 --- /dev/null +++ b/x/slashing/internal/types/expected_keepers.go @@ -0,0 +1,28 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" +) + +// StakingKeeper expected staking keeper +type StakingKeeper interface { + // iterate through validators by operator address, execute func for each validator + IterateValidators(sdk.Context, + func(index int64, validator stakingexported.ValidatorI) (stop bool)) + + Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address + + // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction + Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) + Jail(sdk.Context, sdk.ConsAddress) // jail a validator + Unjail(sdk.Context, sdk.ConsAddress) // unjail a validator + + // Delegation allows for getting a particular delegation for a given validator + // and delegator outside the scope of the staking module. + Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI + + // MaxValidators returns the maximum amount of bonded validators + MaxValidators(sdk.Context) uint16 +} diff --git a/x/slashing/module.go b/x/slashing/module.go new file mode 100644 index 000000000..3ae0ca8cd --- /dev/null +++ b/x/slashing/module.go @@ -0,0 +1,124 @@ +package slashing + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/terra-project/core/x/slashing/internal/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module for slashing +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, stakingKeeper types.StakingKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper, stakingKeeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/staking/alias.go b/x/staking/alias.go new file mode 100644 index 000000000..c090df039 --- /dev/null +++ b/x/staking/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/staking/internal/types/ +package staking + +import ( + "github.com/terra-project/core/x/staking/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/staking/client/cli/cli_test.go b/x/staking/client/cli/cli_test.go deleted file mode 100644 index 9d68f2fce..000000000 --- a/x/staking/client/cli/cli_test.go +++ /dev/null @@ -1,402 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/spf13/cobra" - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" -) - -func TestCreateValidatorTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - stakingTxCmd := &cobra.Command{ - Use: "staking", - Short: "Querying commands for the staking module", - } - - txCmd.AddCommand(stakingTxCmd) - - stakingTxCmd.AddCommand(client.PostCommands( - GetCmdCreateValidator(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `staking`, - `create-validator`, - `--amount=1000000uluna`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--pubkey=terravalconspub1zcjduepqp4hglzr4m4u9lnxxp7f4tv9r0gt86jpcfk84m8fzhke4r3u7ctzsgxgpj5`, - `--moniker=validator`, - `--identity=identity-url`, - `--details=details-description`, - `--website=website-url`, - `--commission-rate=0.10`, - `--commission-max-rate=0.20`, - `--commission-max-change-rate=0.01`, - `--min-self-delegation=1`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestEditValidatorTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - stakingTxCmd := &cobra.Command{ - Use: "staking", - Short: "Querying commands for the staking module", - } - - txCmd.AddCommand(stakingTxCmd) - - stakingTxCmd.AddCommand(client.PostCommands( - GetCmdEditValidator(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `staking`, - `edit-validator`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--moniker=validator`, - `--identity=identity-url`, - `--details=details-description`, - `--website=website-url`, - `--commission-rate=0.10`, - `--min-self-delegation=1`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestDelegateTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - stakingTxCmd := &cobra.Command{ - Use: "staking", - Short: "Querying commands for the staking module", - } - - txCmd.AddCommand(stakingTxCmd) - - stakingTxCmd.AddCommand(client.PostCommands( - GetCmdDelegate(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `staking`, - `delegate`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--validator=terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l`, - `--amount=100000000uluna`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestRedelegateTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - stakingTxCmd := &cobra.Command{ - Use: "staking", - Short: "Querying commands for the staking module", - } - - txCmd.AddCommand(stakingTxCmd) - - stakingTxCmd.AddCommand(client.PostCommands( - GetCmdRedelegate(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `staking`, - `redelegate`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--addr-validator-source=terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l`, - `--addr-validator-dest=terravaloper1yg5gcx9krjhq0at036uyjg2h0cpxwt5g746drv`, - `--amount=100000000uluna`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestUnbondTx(t *testing.T) { - cdc, rootCmd, txCmd, _ := testutil.PrepareCmdTest() - - stakingTxCmd := &cobra.Command{ - Use: "staking", - Short: "Querying commands for the staking module", - } - - txCmd.AddCommand(stakingTxCmd) - - stakingTxCmd.AddCommand(client.PostCommands( - GetCmdUnbond(cdc), - )...) - - // normal case all parameter given - _, err := testutil.ExecuteCommand( - rootCmd, - `tx`, - `staking`, - `unbond`, - `--from=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv`, - `--validator=terravaloper1wg2mlrxdmnnkkykgqg4znky86nyrtc45q7a85l`, - `--amount=100000000uluna`, - `--generate-only`, - `--offline`, - `--chain-id=columbus`, - ) - - require.Nil(t, err) -} - -func TestQueryValidator(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidator := GetCmdQueryValidator(staking.StoreKey, cdc) - - // Name check - require.Equal(t, staking.QueryValidator, queryValidator.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidator.Args)) - - // Check Flags - validatorFlag := queryValidator.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryValidators(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidators := GetCmdQueryValidators(staking.StoreKey, cdc) - - // Name check - require.Equal(t, staking.QueryValidators, queryValidators.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidators.Args)) -} - -func TestQueryValidatorUnbondingDelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorUnbondingDelegations := GetCmdQueryValidatorUnbondingDelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "unbonding-delegations-from", queryValidatorUnbondingDelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorUnbondingDelegations.Args)) - - // Check Flags - validatorFlag := queryValidatorUnbondingDelegations.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryValidatorRedelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorRedelegations := GetCmdQueryValidatorRedelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "redelegations-from", queryValidatorRedelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorRedelegations.Args)) - - // Check Flags - validatorFlag := queryValidatorRedelegations.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryDelegation(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryDelegation := GetCmdQueryDelegation(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "delegation", queryDelegation.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryDelegation.Args)) - - // Check Flags - validatorFlag := queryDelegation.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - delegatorFlag := queryDelegation.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryDelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryDelegations := GetCmdQueryDelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "delegations", queryDelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryDelegations.Args)) - - // Check Flags - delegatorFlag := queryDelegations.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryValidatorDelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryValidatorDelegations := GetCmdQueryValidatorDelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "delegations-to", queryValidatorDelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryValidatorDelegations.Args)) - - // Check Flags - validatorFlag := queryValidatorDelegations.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryUnbondingDelegation(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryUnbondingDelegation := GetCmdQueryUnbondingDelegation(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "unbonding-delegation", queryUnbondingDelegation.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryUnbondingDelegation.Args)) - - // Check Flags - validatorFlag := queryUnbondingDelegation.Flag(flagAddressValidator) - require.NotNil(t, validatorFlag) - require.Equal(t, []string{"true"}, validatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - delegatorFlag := queryUnbondingDelegation.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryUnbondingDelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryUnbondingDelegations := GetCmdQueryUnbondingDelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "unbonding-delegations", queryUnbondingDelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryUnbondingDelegations.Args)) - - // Check Flags - delegatorFlag := queryUnbondingDelegations.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryRedelegation(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryRedelegation := GetCmdQueryRedelegation(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "redelegation", queryRedelegation.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryRedelegation.Args)) - - // Check Flags - delegatorFlag := queryRedelegation.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - validatorSrcFlag := queryRedelegation.Flag(flagAddressValidatorSrc) - require.NotNil(t, validatorSrcFlag) - require.Equal(t, []string{"true"}, validatorSrcFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - validatorDstFlag := queryRedelegation.Flag(flagAddressValidatorDst) - require.NotNil(t, validatorDstFlag) - require.Equal(t, []string{"true"}, validatorDstFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryRedelegations(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryRedelegations := GetCmdQueryRedelegations(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "redelegations", queryRedelegations.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryRedelegations.Args)) - - // Check Flags - delegatorFlag := queryRedelegations.Flag(flagAddressDelegator) - require.NotNil(t, delegatorFlag) - require.Equal(t, []string{"true"}, delegatorFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryPool(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryPool := GetCmdQueryPool(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "pool", queryPool.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryPool.Args)) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParams := GetCmdQueryParams(staking.StoreKey, cdc) - - // Name check - require.Equal(t, "params", queryParams.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParams.Args)) -} diff --git a/x/staking/client/cli/flags.go b/x/staking/client/cli/flags.go deleted file mode 100755 index f63fda8fa..000000000 --- a/x/staking/client/cli/flags.go +++ /dev/null @@ -1,75 +0,0 @@ -package cli - -import ( - flag "github.com/spf13/pflag" - - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// nolint -const ( - flagAddressValidator = "validator" - flagAddressValidatorSrc = "addr-validator-source" - flagAddressValidatorDst = "addr-validator-dest" - flagAddressDelegator = "delegator" - flagPubKey = "pubkey" - flagAmount = "amount" - flagSharesAmount = "shares-amount" - flagSharesFraction = "shares-fraction" - - flagMoniker = "moniker" - flagIdentity = "identity" - flagWebsite = "website" - flagDetails = "details" - - flagCommissionRate = "commission-rate" - flagCommissionMaxRate = "commission-max-rate" - flagCommissionMaxChangeRate = "commission-max-change-rate" - - flagMinSelfDelegation = "min-self-delegation" - - flagGenesisFormat = "genesis-format" - flagNodeID = "node-id" - flagIP = "ip" - - flagOffline = "offline" -) - -// common flagsets to add to various functions -var ( - fsPk = flag.NewFlagSet("", flag.ContinueOnError) - fsAmount = flag.NewFlagSet("", flag.ContinueOnError) - fsShares = flag.NewFlagSet("", flag.ContinueOnError) - fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError) - fsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) - fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError) - fsMinSelfDelegation = flag.NewFlagSet("", flag.ContinueOnError) - fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError) - fsValidator = flag.NewFlagSet("", flag.ContinueOnError) - fsDelegator = flag.NewFlagSet("", flag.ContinueOnError) - fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError) -) - -func init() { - fsPk.String(flagPubKey, "", "The Bech32 encoded PubKey of the validator") - fsAmount.String(flagAmount, "", "Amount of coins to bond") - fsShares.String(flagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") - fsShares.String(flagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") - fsDescriptionCreate.String(flagMoniker, "", "The validator's name") - fsDescriptionCreate.String(flagIdentity, "", "The optional identity signature (ex. UPort or Keybase)") - fsDescriptionCreate.String(flagWebsite, "", "The validator's (optional) website") - fsDescriptionCreate.String(flagDetails, "", "The validator's (optional) details") - fsCommissionUpdate.String(flagCommissionRate, "", "The new commission rate percentage") - fsCommissionCreate.String(flagCommissionRate, "", "The initial commission rate percentage") - fsCommissionCreate.String(flagCommissionMaxRate, "", "The maximum commission rate percentage") - fsCommissionCreate.String(flagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") - fsMinSelfDelegation.String(flagMinSelfDelegation, "", "The minimum self delegation required on the validator") - fsDescriptionEdit.String(flagMoniker, types.DoNotModifyDesc, "The validator's name") - fsDescriptionEdit.String(flagIdentity, types.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)") - fsDescriptionEdit.String(flagWebsite, types.DoNotModifyDesc, "The validator's (optional) website") - fsDescriptionEdit.String(flagDetails, types.DoNotModifyDesc, "The validator's (optional) details") - fsValidator.String(flagAddressValidator, "", "The Bech32 address of the validator") - fsDelegator.String(flagAddressDelegator, "", "The Bech32 address of the delegator") - fsRedelegation.String(flagAddressValidatorSrc, "", "The Bech32 address of the source validator") - fsRedelegation.String(flagAddressValidatorDst, "", "The Bech32 address of the destination validator") -} diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go deleted file mode 100755 index 3ad7ffaf3..000000000 --- a/x/staking/client/cli/query.go +++ /dev/null @@ -1,528 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// GetCmdQueryValidator implements the validator query command. -func GetCmdQueryValidator(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "validator --validator [validator-addr]", - Args: cobra.NoArgs, - Short: "Query a validator", - Long: strings.TrimSpace(`Query details about an individual validator: - -$ terracli query staking validator --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - addrStr := viper.GetString(flagAddressValidator) - addr, err := sdk.ValAddressFromBech32(addrStr) - if err != nil { - return err - } - - res, err := cliCtx.QueryStore(staking.GetValidatorKey(addr), storeName) - if err != nil { - return err - } - - if len(res) == 0 { - return fmt.Errorf("No validator found with address %s", addr) - } - - return cliCtx.PrintOutput(types.MustUnmarshalValidator(cdc, res)) - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryValidators implements the query all validators command. -func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "validators", - Short: "Query for all validators", - Args: cobra.NoArgs, - Long: strings.TrimSpace(`Query details about all validators on a network: - -$ terracli query staking validators -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - resKVs, err := cliCtx.QuerySubspace(staking.ValidatorsKey, storeName) - if err != nil { - return err - } - - var validators staking.Validators - for _, kv := range resKVs { - validators = append(validators, types.MustUnmarshalValidator(cdc, kv.Value)) - } - - return cliCtx.PrintOutput(validators) - }, - } -} - -// GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. -func GetCmdQueryValidatorUnbondingDelegations(storeKey string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "unbonding-delegations-from --validator [validator-addr]", - Short: "Query all unbonding delegatations from a validator", - Long: strings.TrimSpace(`Query delegations that are unbonding _from_ a validator: - -$ terracli query staking unbonding-delegations-from --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - addrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(addrStr) - if err != nil { - return err - } - - bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(valAddr)) - if err != nil { - return err - } - - route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryValidatorUnbondingDelegations) - res, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err - } - - var ubds staking.UnbondingDelegations - cdc.MustUnmarshalJSON(res, &ubds) - return cliCtx.PrintOutput(ubds) - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryValidatorRedelegations implements the query all redelegatations from a validator command. -func GetCmdQueryValidatorRedelegations(storeKey string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "redelegations-from --validator [validator-addr]", - Short: "Query all outgoing redelegatations from a validator", - Long: strings.TrimSpace(`Query delegations that are redelegating _from_ a validator: - -$ terrali query staking redelegations-from --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - addrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(addrStr) - if err != nil { - return err - } - - bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(valAddr)) - if err != nil { - return err - } - - route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryValidatorRedelegations) - res, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err - } - - var reds staking.Redelegations - cdc.MustUnmarshalJSON(res, &reds) - return cliCtx.PrintOutput(reds) - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryDelegation the query delegation command. -func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "delegation --delegator [delegator-addr] --validator [validator-addr]", - Short: "Query a delegation based on address and validator address", - Long: strings.TrimSpace(`Query delegations for an individual delegator on an individual validator: - -$ terracli query staking delegation --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - delAddrStr := viper.GetString(flagAddressDelegator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - res, err := cliCtx.QueryStore(staking.GetDelegationKey(delAddr, valAddr), storeName) - if err != nil { - return err - } - - delegation, err := types.UnmarshalDelegation(cdc, res) - if err != nil { - return err - } - - return cliCtx.PrintOutput(delegation) - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - cmd.Flags().AddFlagSet(fsDelegator) - - cmd.MarkFlagRequired(flagAddressValidator) - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} - -// GetCmdQueryDelegations implements the command to query all the delegations -// made from one delegator. -func GetCmdQueryDelegations(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "delegations --delegator [delegator-addr]", - Short: "Query all delegations made by one delegator", - Long: strings.TrimSpace(`Query delegations for an individual delegator on all validators: - -$ terracli query staking delegations --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddrStr := viper.GetString(flagAddressValidator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - resKVs, err := cliCtx.QuerySubspace(staking.GetDelegationsKey(delAddr), storeName) - if err != nil { - return err - } - - var delegations staking.Delegations - for _, kv := range resKVs { - delegations = append(delegations, types.MustUnmarshalDelegation(cdc, kv.Value)) - } - - return cliCtx.PrintOutput(delegations) - }, - } - - cmd.Flags().AddFlagSet(fsDelegator) - - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} - -// GetCmdQueryValidatorDelegations implements the command to query all the -// delegations to a specific validator. -func GetCmdQueryValidatorDelegations(storeKey string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "delegations-to --validator [validator-addr]", - Short: "Query all delegations made to one validator", - Long: strings.TrimSpace(`Query delegations on an individual validator: - -$ terracli query staking delegations-to --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - validatorAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - bz, err := cdc.MarshalJSON(staking.NewQueryValidatorParams(validatorAddr)) - if err != nil { - return err - } - - route := fmt.Sprintf("custom/%s/%s", storeKey, staking.QueryValidatorDelegations) - res, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err - } - - var dels staking.Delegations - cdc.MustUnmarshalJSON(res, &dels) - return cliCtx.PrintOutput(dels) - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAddressValidator) - - return cmd -} - -// GetCmdQueryUnbondingDelegation implements the command to query a single -// unbonding-delegation record. -func GetCmdQueryUnbondingDelegation(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "unbonding-delegation --delegator [delegator-addr] --validator [validator-addr]", - Args: cobra.NoArgs, - Short: "Query an unbonding-delegation record based on delegator and validator address", - Long: strings.TrimSpace(`Query unbonding delegations for an individual delegator on an individual validator: - -$ terracli query staking unbonding-delegation --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - delAddrStr := viper.GetString(flagAddressDelegator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - res, err := cliCtx.QueryStore(staking.GetUBDKey(delAddr, valAddr), storeName) - if err != nil { - return err - } - - return cliCtx.PrintOutput(types.MustUnmarshalUBD(cdc, res)) - - }, - } - - cmd.Flags().AddFlagSet(fsValidator) - cmd.Flags().AddFlagSet(fsDelegator) - - cmd.MarkFlagRequired(flagAddressValidator) - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} - -// GetCmdQueryUnbondingDelegations implements the command to query all the -// unbonding-delegation records for a delegator. -func GetCmdQueryUnbondingDelegations(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "unbonding-delegations --delegator [delegator-addr]", - Args: cobra.NoArgs, - Short: "Query all unbonding-delegations records for one delegator", - Long: strings.TrimSpace(`Query unbonding delegations for an individual delegator: - -$ terracli query staking unbonding-delegation --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddrStr := viper.GetString(flagAddressDelegator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - resKVs, err := cliCtx.QuerySubspace(staking.GetUBDsKey(delAddr), storeName) - if err != nil { - return err - } - - var ubds staking.UnbondingDelegations - for _, kv := range resKVs { - ubds = append(ubds, types.MustUnmarshalUBD(cdc, kv.Value)) - } - - return cliCtx.PrintOutput(ubds) - }, - } - - cmd.Flags().AddFlagSet(fsDelegator) - - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} - -// GetCmdQueryRedelegation implements the command to query a single -// redelegation record. -func GetCmdQueryRedelegation(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "redelegation --delegator [delegator-addr] --addr-validator-source [src-validator-addr] --addr-validator-dest [dst-validator-addr]", - Args: cobra.NoArgs, - Short: "Query a redelegation record based on delegator and a source and destination validator address", - Long: strings.TrimSpace(`Query a redelegation record for an individual delegator between a source and destination validator: - -$ terracli query staking redelegation --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --addr-validator-source terravaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm --addr-validator-dest terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valSrcAddrStr := viper.GetString(flagAddressValidatorSrc) - valSrcAddr, err := sdk.ValAddressFromBech32(valSrcAddrStr) - if err != nil { - return err - } - - valDstAddrStr := viper.GetString(flagAddressValidatorDst) - valDstAddr, err := sdk.ValAddressFromBech32(valDstAddrStr) - if err != nil { - return err - } - - delAddrStr := viper.GetString(flagAddressDelegator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - res, err := cliCtx.QueryStore(staking.GetREDKey(delAddr, valSrcAddr, valDstAddr), storeName) - if err != nil { - return err - } - - return cliCtx.PrintOutput(types.MustUnmarshalRED(cdc, res)) - }, - } - - cmd.Flags().AddFlagSet(fsDelegator) - cmd.Flags().AddFlagSet(fsRedelegation) - - cmd.MarkFlagRequired(flagAddressDelegator) - cmd.MarkFlagRequired(flagAddressValidatorDst) - cmd.MarkFlagRequired(flagAddressValidatorSrc) - - return cmd -} - -// GetCmdQueryRedelegations implements the command to query all the -// redelegation records for a delegator. -func GetCmdQueryRedelegations(storeName string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "redelegations --delegator [delegator-addr]", - Args: cobra.NoArgs, - Short: "Query all redelegations records for one delegator", - Long: strings.TrimSpace(`Query all redelegation records for an individual delegator: - -$ terracli query staking redelegations --delegator terra1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddrStr := viper.GetString(flagAddressDelegator) - delAddr, err := sdk.AccAddressFromBech32(delAddrStr) - if err != nil { - return err - } - - resKVs, err := cliCtx.QuerySubspace(staking.GetREDsKey(delAddr), storeName) - if err != nil { - return err - } - - var reds staking.Redelegations - for _, kv := range resKVs { - reds = append(reds, types.MustUnmarshalRED(cdc, kv.Value)) - } - - return cliCtx.PrintOutput(reds) - }, - } - - cmd.Flags().AddFlagSet(fsDelegator) - - cmd.MarkFlagRequired(flagAddressDelegator) - - return cmd -} - -// GetCmdQueryPool implements the pool query command. -func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "pool", - Args: cobra.NoArgs, - Short: "Query the current staking pool values", - Long: strings.TrimSpace(`Query values for amounts stored in the staking pool: - -$ terracli query staking pool -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryStore(staking.PoolKey, storeName) - if err != nil { - return err - } - - return cliCtx.PrintOutput(types.MustUnmarshalPool(cdc, res)) - }, - } -} - -// GetCmdQueryPool implements the params query command. -func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "params", - Args: cobra.NoArgs, - Short: "Query the current staking parameters information", - Long: strings.TrimSpace(`Query values set as staking parameters: - -$ terracli query staking params -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", storeName, staking.QueryParameters) - bz, err := cliCtx.QueryWithData(route, nil) - if err != nil { - return err - } - - var params staking.Params - cdc.MustUnmarshalJSON(bz, ¶ms) - return cliCtx.PrintOutput(params) - }, - } -} diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go deleted file mode 100755 index c08244651..000000000 --- a/x/staking/client/cli/tx.go +++ /dev/null @@ -1,342 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/staking" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -// GetCmdCreateValidator implements the create validator command handler. -// TODO: Add full description -func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "create-validator", - Args: cobra.NoArgs, - Short: "create new validator initialized with a self-delegation to it", - Long: ` - terracli tx staking create-validator \ - --amount=5000000uluna \ - --pubkey=$(terrad tendermint show-validator) \ - --moniker="choose a moniker" \ - --chain-id= \ - --from= \ - --commission-rate="0.10" \ - --commission-max-rate="0.20" \ - --commission-max-change-rate="0.01" \ - --min-self-delegation="1"`, - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) - if err != nil { - return err - } - - offline := viper.GetBool(flagOffline) - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().AddFlagSet(fsPk) - cmd.Flags().AddFlagSet(fsAmount) - cmd.Flags().AddFlagSet(fsDescriptionCreate) - cmd.Flags().AddFlagSet(fsCommissionCreate) - cmd.Flags().AddFlagSet(fsMinSelfDelegation) - - cmd.Flags().String(flagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", client.FlagGenerateOnly)) - cmd.Flags().String(flagNodeID, "", "The node's ID") - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - cmd.MarkFlagRequired(client.FlagFrom) - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagPubKey) - cmd.MarkFlagRequired(flagMoniker) - - return cmd -} - -// GetCmdEditValidator implements the create edit validator command. -// TODO: add full description -func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "edit-validator", - Args: cobra.NoArgs, - Short: "edit an existing validator account", - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - valAddr := cliCtx.GetFromAddress() - description := staking.Description{ - Moniker: viper.GetString(flagMoniker), - Identity: viper.GetString(flagIdentity), - Website: viper.GetString(flagWebsite), - Details: viper.GetString(flagDetails), - } - - var newRate *sdk.Dec - - commissionRate := viper.GetString(flagCommissionRate) - if commissionRate != "" { - rate, err := sdk.NewDecFromStr(commissionRate) - if err != nil { - return fmt.Errorf("invalid new commission rate: %v", err) - } - - newRate = &rate - } - - var newMinSelfDelegation *sdk.Int - - minSelfDelegationString := viper.GetString(flagMinSelfDelegation) - if minSelfDelegationString != "" { - msb, ok := sdk.NewIntFromString(minSelfDelegationString) - if !ok { - return fmt.Errorf(staking.ErrMinSelfDelegationInvalid(staking.DefaultCodespace).Error()) - } - newMinSelfDelegation = &msb - } - - msg := staking.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) - - offline := viper.GetBool(flagOffline) - - // build and sign the transaction, then broadcast to Tendermint - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().AddFlagSet(fsDescriptionEdit) - cmd.Flags().AddFlagSet(fsCommissionUpdate) - cmd.Flags().AddFlagSet(fsMinSelfDelegation) - - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -// GetCmdDelegate implements the delegate command. -func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "delegate --validator [validator-addr] --amount [amount]", - Args: cobra.NoArgs, - Short: "delegate liquid tokens to a validator", - Long: strings.TrimSpace(`Delegate an amount of liquid coins to a validator from your wallet: - -$ terracli tx staking delegate --validator terravaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm --amount 1000uluna --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - amountStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoin(amountStr) - if err != nil { - return err - } - - delAddr := cliCtx.GetFromAddress() - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - msg := staking.NewMsgDelegate(delAddr, valAddr, amount) - - offline := viper.GetBool(flagOffline) - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().AddFlagSet(fsAmount) - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagAddressValidator) - - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -// GetCmdRedelegate the begin redelegation command. -func GetCmdRedelegate(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "redelegate --addr-validator-source [src-validator-addr] --addr-validator-dest [dst-validator-addr] --amount [amount]", - Args: cobra.NoArgs, - Short: "redelegate illiquid tokens from one validator to another", - Long: strings.TrimSpace(`Redelegate an amount of illiquid staking tokens from one validator to another: - -$ terracli tx staking redelegate --addr-validator-source terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --addr-validator-dest terravaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm --amount 100uluna --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - delAddr := cliCtx.GetFromAddress() - - valSrcAddrStr := viper.GetString(flagAddressValidatorSrc) - valSrcAddr, err := sdk.ValAddressFromBech32(valSrcAddrStr) - if err != nil { - return err - } - - valDstAddrStr := viper.GetString(flagAddressValidatorDst) - valDstAddr, err := sdk.ValAddressFromBech32(valDstAddrStr) - if err != nil { - return err - } - - amountStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoin(amountStr) - if err != nil { - return err - } - - msg := staking.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) - - offline := viper.GetBool(flagOffline) - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().AddFlagSet(fsAmount) - cmd.Flags().AddFlagSet(fsRedelegation) - - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagAddressValidatorSrc) - cmd.MarkFlagRequired(flagAddressValidatorDst) - - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -// GetCmdUnbond implements the unbond validator command. -func GetCmdUnbond(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "unbond --validator [validator-addr] --amount [amount]", - Args: cobra.NoArgs, - Short: "unbond shares from a validator", - Long: strings.TrimSpace(`Unbond an amount of bonded shares from a validator: - -$ terracli tx staking unbond --validator terravaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --amount 100uluna --from mykey -`), - RunE: func(cmd *cobra.Command, args []string) error { - txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithAccountDecoder(cdc) - - delAddr := cliCtx.GetFromAddress() - - valAddrStr := viper.GetString(flagAddressValidator) - valAddr, err := sdk.ValAddressFromBech32(valAddrStr) - if err != nil { - return err - } - - amountStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoin(amountStr) - if err != nil { - return err - } - - msg := staking.NewMsgUndelegate(delAddr, valAddr, amount) - - offline := viper.GetBool(flagOffline) - - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, offline) - }, - } - - cmd.Flags().AddFlagSet(fsAmount) - cmd.Flags().AddFlagSet(fsValidator) - - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagAddressValidator) - - cmd.Flags().Bool(flagOffline, false, " Offline mode; Without full node connection it can build and sign tx") - - return cmd -} - -// BuildCreateValidatorMsg makes a new MsgCreateValidator. -func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder) (authtxb.TxBuilder, sdk.Msg, error) { - amounstStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoin(amounstStr) - if err != nil { - return txBldr, nil, err - } - - valAddr := cliCtx.GetFromAddress() - pkStr := viper.GetString(flagPubKey) - - pk, err := sdk.GetConsPubKeyBech32(pkStr) - if err != nil { - return txBldr, nil, err - } - - description := staking.NewDescription( - viper.GetString(flagMoniker), - viper.GetString(flagIdentity), - viper.GetString(flagWebsite), - viper.GetString(flagDetails), - ) - - // get the initial validator commission parameters - rateStr := viper.GetString(flagCommissionRate) - maxRateStr := viper.GetString(flagCommissionMaxRate) - maxChangeRateStr := viper.GetString(flagCommissionMaxChangeRate) - commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr) - if err != nil { - return txBldr, nil, err - } - - // get the initial validator min self delegation - msbStr := viper.GetString(flagMinSelfDelegation) - minSelfDelegation, ok := sdk.NewIntFromString(msbStr) - if !ok { - return txBldr, nil, fmt.Errorf(staking.ErrMinSelfDelegationInvalid(staking.DefaultCodespace).Error()) - } - - msg := staking.NewMsgCreateValidator( - sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation, - ) - - if viper.GetBool(client.FlagGenerateOnly) { - ip := viper.GetString(flagIP) - nodeID := viper.GetString(flagNodeID) - if nodeID != "" && ip != "" { - txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) - } - } - - return txBldr, msg, nil -} diff --git a/x/staking/client/cli/utils.go b/x/staking/client/cli/utils.go deleted file mode 100755 index 1cd897c27..000000000 --- a/x/staking/client/cli/utils.go +++ /dev/null @@ -1,32 +0,0 @@ -package cli - -import ( - "errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr string) (commission types.CommissionMsg, err error) { - if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" { - return commission, errors.New("must specify all validator commission parameters") - } - - rate, err := sdk.NewDecFromStr(rateStr) - if err != nil { - return commission, err - } - - maxRate, err := sdk.NewDecFromStr(maxRateStr) - if err != nil { - return commission, err - } - - maxChangeRate, err := sdk.NewDecFromStr(maxChangeRateStr) - if err != nil { - return commission, err - } - - commission = types.NewCommissionMsg(rate, maxRate, maxChangeRate) - return commission, nil -} diff --git a/x/staking/client/module_client.go b/x/staking/client/module_client.go deleted file mode 100755 index 7bd7a924b..000000000 --- a/x/staking/client/module_client.go +++ /dev/null @@ -1,64 +0,0 @@ -package client - -import ( - "github.com/spf13/cobra" - amino "github.com/tendermint/go-amino" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/terra-project/core/x/staking/client/cli" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - stakingQueryCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Querying commands for the staking module", - } - stakingQueryCmd.AddCommand(client.GetCommands( - cli.GetCmdQueryDelegation(mc.storeKey, mc.cdc), - cli.GetCmdQueryDelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryUnbondingDelegation(mc.storeKey, mc.cdc), - cli.GetCmdQueryUnbondingDelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryRedelegation(mc.storeKey, mc.cdc), - cli.GetCmdQueryRedelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryValidator(mc.storeKey, mc.cdc), - cli.GetCmdQueryValidators(mc.storeKey, mc.cdc), - cli.GetCmdQueryValidatorDelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryValidatorUnbondingDelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryValidatorRedelegations(mc.storeKey, mc.cdc), - cli.GetCmdQueryParams(mc.storeKey, mc.cdc), - cli.GetCmdQueryPool(mc.storeKey, mc.cdc))...) - - return stakingQueryCmd - -} - -// GetTxCmd returns the transaction commands for this module -func (mc ModuleClient) GetTxCmd() *cobra.Command { - stakingTxCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Staking transaction subcommands", - } - - stakingTxCmd.AddCommand(client.PostCommands( - cli.GetCmdCreateValidator(mc.cdc), - cli.GetCmdEditValidator(mc.cdc), - cli.GetCmdDelegate(mc.cdc), - cli.GetCmdRedelegate(mc.cdc), - cli.GetCmdUnbond(mc.cdc), - )...) - - return stakingTxCmd -} diff --git a/x/staking/client/module_client_test.go b/x/staking/client/module_client_test.go deleted file mode 100644 index d5e271008..000000000 --- a/x/staking/client/module_client_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "params": true, - "validator": true, - "validators": true, - "unbonding-delegations-from": true, - "redelegations-from": true, - "delegation": true, - "delegations": true, - "delegations-to": true, - "unbonding-delegation": true, - "unbonding-delegations": true, - "redelegation": true, - "redelegations": true, - "pool": true, - } - - txCmdList = map[string]bool{ - "create-validator": true, - "edit-validator": true, - "delegate": true, - "redelegate": true, - "unbond": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} - -func TestTxCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetTxCmd().Commands() { - _, ok := txCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(txCmdList), len(mc.GetTxCmd().Commands())) -} diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go deleted file mode 100755 index a9439bd80..000000000 --- a/x/staking/client/rest/query.go +++ /dev/null @@ -1,322 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - "strings" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/tags" - - "github.com/gorilla/mux" -) - -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - // Get all delegations from a delegator - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/delegations", - delegatorDelegationsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get all unbonding delegations from a delegator - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/unbonding_delegations", - delegatorUnbondingDelegationsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get all staking txs (i.e msgs) from a delegator - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/txs", - delegatorTxsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Query all validators that a delegator is bonded to - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/validators", - delegatorValidatorsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Query a validator that a delegator is bonded to - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/validators/{validatorAddr}", - delegatorValidatorHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Query a delegation between a delegator and a validator - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/delegations/{validatorAddr}", - delegationHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Query all unbonding delegations between a delegator and a validator - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", - unbondingDelegationHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Query redelegations (filters in query params) - r.HandleFunc( - "/staking/redelegations", - redelegationsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get all validators - r.HandleFunc( - "/staking/validators", - validatorsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get a single validator info - r.HandleFunc( - "/staking/validators/{validatorAddr}", - validatorHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get all delegations to a validator - r.HandleFunc( - "/staking/validators/{validatorAddr}/delegations", - validatorDelegationsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get all unbonding delegations from a validator - r.HandleFunc( - "/staking/validators/{validatorAddr}/unbonding_delegations", - validatorUnbondingDelegationsHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get the current state of the staking pool - r.HandleFunc( - "/staking/pool", - poolHandlerFn(cliCtx, cdc), - ).Methods("GET") - - // Get the current staking parameter values - r.HandleFunc( - "/staking/parameters", - paramsHandlerFn(cliCtx, cdc), - ).Methods("GET") - -} - -// HTTP request handler to query a delegator delegations -func delegatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryDelegator(cliCtx, cdc, "custom/staking/delegatorDelegations") -} - -// HTTP request handler to query a delegator unbonding delegations -func delegatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryDelegator(cliCtx, cdc, "custom/staking/delegatorUnbondingDelegations") -} - -// HTTP request handler to query all staking txs (msgs) from a delegator -func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var typesQuerySlice []string - vars := mux.Vars(r) - delegatorAddr := vars["delegatorAddr"] - - _, err := sdk.AccAddressFromBech32(delegatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - typesQuery := r.URL.Query().Get("type") - trimmedQuery := strings.TrimSpace(typesQuery) - if len(trimmedQuery) != 0 { - typesQuerySlice = strings.Split(trimmedQuery, " ") - } - - noQuery := len(typesQuerySlice) == 0 - isBondTx := contains(typesQuerySlice, "bond") - isUnbondTx := contains(typesQuerySlice, "unbond") - isRedTx := contains(typesQuerySlice, "redelegate") - var txs = []sdk.TxResponse{} - var actions []string - - switch { - case isBondTx: - actions = append(actions, staking.MsgDelegate{}.Type()) - case isUnbondTx: - actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, tags.ActionCompleteUnbonding) - case isRedTx: - actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, tags.ActionCompleteRedelegation) - case noQuery: - actions = append(actions, staking.MsgDelegate{}.Type()) - actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, tags.ActionCompleteUnbonding) - actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, tags.ActionCompleteRedelegation) - default: - w.WriteHeader(http.StatusNoContent) - return - } - - for _, action := range actions { - foundTxs, errQuery := queryTxs(cliCtx, cdc, action, delegatorAddr) - if errQuery != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, errQuery.Error()) - } - txs = append(txs, foundTxs...) - } - - res, err := cdc.MarshalJSON(txs) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query an unbonding-delegation -func unbondingDelegationHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryBonds(cliCtx, cdc, "custom/staking/unbondingDelegation") -} - -// HTTP request handler to query redelegations -func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var params staking.QueryRedelegationParams - - bechDelegatorAddr := r.URL.Query().Get("delegator") - bechSrcValidatorAddr := r.URL.Query().Get("validator_from") - bechDstValidatorAddr := r.URL.Query().Get("validator_to") - - if len(bechDelegatorAddr) != 0 { - delegatorAddr, err := sdk.AccAddressFromBech32(bechDelegatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - params.DelegatorAddr = delegatorAddr - } - - if len(bechSrcValidatorAddr) != 0 { - srcValidatorAddr, err := sdk.ValAddressFromBech32(bechSrcValidatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - params.SrcValidatorAddr = srcValidatorAddr - } - - if len(bechDstValidatorAddr) != 0 { - dstValidatorAddr, err := sdk.ValAddressFromBech32(bechDstValidatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - params.DstValidatorAddr = dstValidatorAddr - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData("custom/staking/redelegations", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query a delegation -func delegationHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryBonds(cliCtx, cdc, "custom/staking/delegation") -} - -// HTTP request handler to query all delegator bonded validators -func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryDelegator(cliCtx, cdc, "custom/staking/delegatorValidators") -} - -// HTTP request handler to get information from a currently bonded validator -func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryBonds(cliCtx, cdc, "custom/staking/delegatorValidator") -} - -// HTTP request handler to query list of validators -func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - _, page, limit, err := rest.ParseHTTPArgs(r) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - // override default limit if it wasn't provided - if l := r.FormValue("limit"); l == "" { - limit = 0 - } - - status := r.FormValue("status") - if status == "" { - status = sdk.BondStatusBonded - } - - params := staking.NewQueryValidatorsParams(page, limit, status) - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - route := fmt.Sprintf("custom/%s/%s", staking.QuerierRoute, staking.QueryValidators) - res, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query the validator information from a given validator address -func validatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryValidator(cliCtx, cdc, "custom/staking/validator") -} - -// HTTP request handler to query all unbonding delegations from a validator -func validatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryValidator(cliCtx, cdc, "custom/staking/validatorDelegations") -} - -// HTTP request handler to query all unbonding delegations from a validator -func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return queryValidator(cliCtx, cdc, "custom/staking/validatorUnbondingDelegations") -} - -// HTTP request handler to query the pool information -func poolHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - res, err := cliCtx.QueryWithData("custom/staking/pool", nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -// HTTP request handler to query the staking params values -func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - res, err := cliCtx.QueryWithData("custom/staking/parameters", nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} diff --git a/x/staking/client/rest/rest.go b/x/staking/client/rest/rest.go deleted file mode 100755 index 507fcb517..000000000 --- a/x/staking/client/rest/rest.go +++ /dev/null @@ -1,15 +0,0 @@ -package rest - -import ( - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - - "github.com/gorilla/mux" -) - -// RegisterRoutes registers staking-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - registerQueryRoutes(cliCtx, r, cdc) - registerTxRoutes(cliCtx, r, cdc, kb) -} diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go deleted file mode 100755 index f42a90c9d..000000000 --- a/x/staking/client/rest/tx.go +++ /dev/null @@ -1,160 +0,0 @@ -package rest - -import ( - "bytes" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - clientrest "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/delegations", - postDelegationsHandlerFn(cdc, kb, cliCtx), - ).Methods("POST") - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/unbonding_delegations", - postUnbondingDelegationsHandlerFn(cdc, kb, cliCtx), - ).Methods("POST") - r.HandleFunc( - "/staking/delegators/{delegatorAddr}/redelegations", - postRedelegationsHandlerFn(cdc, kb, cliCtx), - ).Methods("POST") -} - -type ( - // DelegateRequest defines the properties of a delegation request's body. - DelegateRequest struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32 - Amount sdk.Coin `json:"amount"` - } - - // RedelegateRequest defines the properties of a redelegate request's body. - RedelegateRequest struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` // in bech32 - ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` // in bech32 - Amount sdk.Coin `json:"amount"` - } - - // UndelegateRequest defines the properties of a undelegate request's body. - UndelegateRequest struct { - BaseReq rest.BaseReq `json:"base_req"` - DelegatorAddress sdk.AccAddress `json:"delegator_address"` // in bech32 - ValidatorAddress sdk.ValAddress `json:"validator_address"` // in bech32 - Amount sdk.Coin `json:"amount"` - } -) - -func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req DelegateRequest - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - msg := staking.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - if !bytes.Equal(fromAddr, req.DelegatorAddress) { - rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req RedelegateRequest - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - msg := staking.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - if !bytes.Equal(fromAddr, req.DelegatorAddress) { - rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} - -func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req UndelegateRequest - - if !rest.ReadRESTReq(w, r, cdc, &req) { - return - } - - req.BaseReq = req.BaseReq.Sanitize() - if !req.BaseReq.ValidateBasic(w) { - return - } - - msg := staking.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - if !bytes.Equal(fromAddr, req.DelegatorAddress) { - rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") - return - } - - clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) - } -} diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go deleted file mode 100755 index a34864e60..000000000 --- a/x/staking/client/rest/utils.go +++ /dev/null @@ -1,124 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/tags" -) - -// contains checks if the a given query contains one of the tx types -func contains(stringSlice []string, txType string) bool { - for _, word := range stringSlice { - if word == txType { - return true - } - } - return false -} - -// queries staking txs -func queryTxs(cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]sdk.TxResponse, error) { - page := 1 - limit := 100 - tags := []string{ - fmt.Sprintf("%s='%s'", tags.Action, tag), - fmt.Sprintf("%s='%s'", tags.Delegator, delegatorAddr), - } - - return tx.SearchTxs(cliCtx, cdc, tags, page, limit) -} - -func queryBonds(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32delegator := vars["delegatorAddr"] - bech32validator := vars["validatorAddr"] - - delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := staking.NewQueryBondsParams(delegatorAddr, validatorAddr) - - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func queryDelegator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32delegator := vars["delegatorAddr"] - - delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := staking.NewQueryDelegatorParams(delegatorAddr) - - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} - -func queryValidator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32validatorAddr := vars["validatorAddr"] - - validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - params := staking.NewQueryValidatorParams(validatorAddr) - - bz, err := cdc.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) - } -} diff --git a/x/staking/cosmos_alias.go b/x/staking/cosmos_alias.go new file mode 100644 index 000000000..d217af635 --- /dev/null +++ b/x/staking/cosmos_alias.go @@ -0,0 +1,241 @@ +// nolint +package staking + +import ( + "github.com/cosmos/cosmos-sdk/x/staking" +) + +const ( + DefaultParamspace = staking.DefaultParamspace + DefaultCodespace = staking.DefaultCodespace + CodeInvalidValidator = staking.CodeInvalidValidator + CodeInvalidDelegation = staking.CodeInvalidDelegation + CodeInvalidInput = staking.CodeInvalidInput + CodeValidatorJailed = staking.CodeValidatorJailed + CodeInvalidAddress = staking.CodeInvalidAddress + CodeUnauthorized = staking.CodeUnauthorized + CodeInternal = staking.CodeInternal + CodeUnknownRequest = staking.CodeUnknownRequest + ModuleName = staking.ModuleName + StoreKey = staking.StoreKey + TStoreKey = staking.TStoreKey + QuerierRoute = staking.QuerierRoute + RouterKey = staking.RouterKey + DefaultUnbondingTime = staking.DefaultUnbondingTime + DefaultMaxValidators = staking.DefaultMaxValidators + DefaultMaxEntries = staking.DefaultMaxEntries + NotBondedPoolName = staking.NotBondedPoolName + BondedPoolName = staking.BondedPoolName + QueryValidators = staking.QueryValidators + QueryValidator = staking.QueryValidator + QueryDelegatorDelegations = staking.QueryDelegatorDelegations + QueryDelegatorUnbondingDelegations = staking.QueryDelegatorUnbondingDelegations + QueryRedelegations = staking.QueryRedelegations + QueryValidatorDelegations = staking.QueryValidatorDelegations + QueryValidatorRedelegations = staking.QueryValidatorRedelegations + QueryValidatorUnbondingDelegations = staking.QueryValidatorUnbondingDelegations + QueryDelegation = staking.QueryDelegation + QueryUnbondingDelegation = staking.QueryUnbondingDelegation + QueryDelegatorValidators = staking.QueryDelegatorValidators + QueryDelegatorValidator = staking.QueryDelegatorValidator + QueryPool = staking.QueryPool + QueryParameters = staking.QueryParameters + MaxMonikerLength = staking.MaxMonikerLength + MaxIdentityLength = staking.MaxIdentityLength + MaxWebsiteLength = staking.MaxWebsiteLength + MaxDetailsLength = staking.MaxDetailsLength + DoNotModifyDesc = staking.DoNotModifyDesc +) + +var ( + // functions aliases + RegisterInvariants = staking.RegisterInvariants + AllInvariants = staking.AllInvariants + ModuleAccountInvariants = staking.ModuleAccountInvariants + NonNegativePowerInvariant = staking.NonNegativePowerInvariant + PositiveDelegationInvariant = staking.PositiveDelegationInvariant + DelegatorSharesInvariant = staking.DelegatorSharesInvariant + NewKeeper = staking.NewKeeper + ParamKeyTable = staking.ParamKeyTable + NewQuerier = staking.NewQuerier + NewCommissionRates = staking.NewCommissionRates + NewCommission = staking.NewCommission + NewCommissionWithTime = staking.NewCommissionWithTime + NewDelegation = staking.NewDelegation + MustMarshalDelegation = staking.MustMarshalDelegation + MustUnmarshalDelegation = staking.MustUnmarshalDelegation + UnmarshalDelegation = staking.UnmarshalDelegation + NewUnbondingDelegation = staking.NewUnbondingDelegation + NewUnbondingDelegationEntry = staking.NewUnbondingDelegationEntry + MustMarshalUBD = staking.MustMarshalUBD + MustUnmarshalUBD = staking.MustUnmarshalUBD + UnmarshalUBD = staking.UnmarshalUBD + NewRedelegation = staking.NewRedelegation + NewRedelegationEntry = staking.NewRedelegationEntry + MustMarshalRED = staking.MustMarshalRED + MustUnmarshalRED = staking.MustUnmarshalRED + UnmarshalRED = staking.UnmarshalRED + NewDelegationResp = staking.NewDelegationResp + NewRedelegationResponse = staking.NewRedelegationResponse + NewRedelegationEntryResponse = staking.NewRedelegationEntryResponse + ErrNilValidatorAddr = staking.ErrNilValidatorAddr + ErrBadValidatorAddr = staking.ErrBadValidatorAddr + ErrNoValidatorFound = staking.ErrNoValidatorFound + ErrValidatorOwnerExists = staking.ErrValidatorOwnerExists + ErrValidatorPubKeyExists = staking.ErrValidatorPubKeyExists + ErrValidatorPubKeyTypeNotSupported = staking.ErrValidatorPubKeyTypeNotSupported + ErrValidatorJailed = staking.ErrValidatorJailed + ErrBadRemoveValidator = staking.ErrBadRemoveValidator + ErrDescriptionLength = staking.ErrDescriptionLength + ErrCommissionNegative = staking.ErrCommissionNegative + ErrCommissionHuge = staking.ErrCommissionHuge + ErrCommissionGTMaxRate = staking.ErrCommissionGTMaxRate + ErrCommissionUpdateTime = staking.ErrCommissionUpdateTime + ErrCommissionChangeRateNegative = staking.ErrCommissionChangeRateNegative + ErrCommissionChangeRateGTMaxRate = staking.ErrCommissionChangeRateGTMaxRate + ErrCommissionGTMaxChangeRate = staking.ErrCommissionGTMaxChangeRate + ErrSelfDelegationBelowMinimum = staking.ErrSelfDelegationBelowMinimum + ErrMinSelfDelegationInvalid = staking.ErrMinSelfDelegationInvalid + ErrMinSelfDelegationDecreased = staking.ErrMinSelfDelegationDecreased + ErrNilDelegatorAddr = staking.ErrNilDelegatorAddr + ErrBadDenom = staking.ErrBadDenom + ErrBadDelegationAddr = staking.ErrBadDelegationAddr + ErrBadDelegationAmount = staking.ErrBadDelegationAmount + ErrNoDelegation = staking.ErrNoDelegation + ErrBadDelegatorAddr = staking.ErrBadDelegatorAddr + ErrNoDelegatorForAddress = staking.ErrNoDelegatorForAddress + ErrInsufficientShares = staking.ErrInsufficientShares + ErrDelegationValidatorEmpty = staking.ErrDelegationValidatorEmpty + ErrNotEnoughDelegationShares = staking.ErrNotEnoughDelegationShares + ErrBadSharesAmount = staking.ErrBadSharesAmount + ErrBadSharesPercent = staking.ErrBadSharesPercent + ErrNotMature = staking.ErrNotMature + ErrNoUnbondingDelegation = staking.ErrNoUnbondingDelegation + ErrMaxUnbondingDelegationEntries = staking.ErrMaxUnbondingDelegationEntries + ErrBadRedelegationAddr = staking.ErrBadRedelegationAddr + ErrNoRedelegation = staking.ErrNoRedelegation + ErrSelfRedelegation = staking.ErrSelfRedelegation + ErrVerySmallRedelegation = staking.ErrVerySmallRedelegation + ErrBadRedelegationDst = staking.ErrBadRedelegationDst + ErrTransitiveRedelegation = staking.ErrTransitiveRedelegation + ErrMaxRedelegationEntries = staking.ErrMaxRedelegationEntries + ErrDelegatorShareExRateInvalid = staking.ErrDelegatorShareExRateInvalid + ErrBothShareMsgsGiven = staking.ErrBothShareMsgsGiven + ErrNeitherShareMsgsGiven = staking.ErrNeitherShareMsgsGiven + ErrMissingSignature = staking.ErrMissingSignature + NewGenesisState = staking.NewGenesisState + DefaultGenesisState = staking.DefaultGenesisState + NewMultiStakingHooks = staking.NewMultiStakingHooks + GetValidatorKey = staking.GetValidatorKey + GetValidatorByConsAddrKey = staking.GetValidatorByConsAddrKey + AddressFromLastValidatorPowerKey = staking.AddressFromLastValidatorPowerKey + GetValidatorsByPowerIndexKey = staking.GetValidatorsByPowerIndexKey + GetLastValidatorPowerKey = staking.GetLastValidatorPowerKey + ParseValidatorPowerRankKey = staking.ParseValidatorPowerRankKey + GetValidatorQueueTimeKey = staking.GetValidatorQueueTimeKey + GetDelegationKey = staking.GetDelegationKey + GetDelegationsKey = staking.GetDelegationsKey + GetUBDKey = staking.GetUBDKey + GetUBDByValIndexKey = staking.GetUBDByValIndexKey + GetUBDKeyFromValIndexKey = staking.GetUBDKeyFromValIndexKey + GetUBDsKey = staking.GetUBDsKey + GetUBDsByValIndexKey = staking.GetUBDsByValIndexKey + GetUnbondingDelegationTimeKey = staking.GetUnbondingDelegationTimeKey + GetREDKey = staking.GetREDKey + GetREDByValSrcIndexKey = staking.GetREDByValSrcIndexKey + GetREDByValDstIndexKey = staking.GetREDByValDstIndexKey + GetREDKeyFromValSrcIndexKey = staking.GetREDKeyFromValSrcIndexKey + GetREDKeyFromValDstIndexKey = staking.GetREDKeyFromValDstIndexKey + GetRedelegationTimeKey = staking.GetRedelegationTimeKey + GetREDsKey = staking.GetREDsKey + GetREDsFromValSrcIndexKey = staking.GetREDsFromValSrcIndexKey + GetREDsToValDstIndexKey = staking.GetREDsToValDstIndexKey + GetREDsByDelToValDstIndexKey = staking.GetREDsByDelToValDstIndexKey + NewMsgCreateValidator = staking.NewMsgCreateValidator + NewMsgEditValidator = staking.NewMsgEditValidator + NewMsgDelegate = staking.NewMsgDelegate + NewMsgBeginRedelegate = staking.NewMsgBeginRedelegate + NewMsgUndelegate = staking.NewMsgUndelegate + NewParams = staking.NewParams + DefaultParams = staking.DefaultParams + MustUnmarshalParams = staking.MustUnmarshalParams + UnmarshalParams = staking.UnmarshalParams + NewPool = staking.NewPool + NewQueryDelegatorParams = staking.NewQueryDelegatorParams + NewQueryValidatorParams = staking.NewQueryValidatorParams + NewQueryBondsParams = staking.NewQueryBondsParams + NewQueryRedelegationParams = staking.NewQueryRedelegationParams + NewQueryValidatorsParams = staking.NewQueryValidatorsParams + NewValidator = staking.NewValidator + MustMarshalValidator = staking.MustMarshalValidator + MustUnmarshalValidator = staking.MustUnmarshalValidator + UnmarshalValidator = staking.UnmarshalValidator + NewDescription = staking.NewDescription + WriteValidators = staking.WriteValidators + NewCosmosAppModule = staking.NewAppModule + + // variable aliases + CosmosModuleCdc = staking.ModuleCdc + LastValidatorPowerKey = staking.LastValidatorPowerKey + LastTotalPowerKey = staking.LastTotalPowerKey + ValidatorsKey = staking.ValidatorsKey + ValidatorsByConsAddrKey = staking.ValidatorsByConsAddrKey + ValidatorsByPowerIndexKey = staking.ValidatorsByPowerIndexKey + DelegationKey = staking.DelegationKey + UnbondingDelegationKey = staking.UnbondingDelegationKey + UnbondingDelegationByValIndexKey = staking.UnbondingDelegationByValIndexKey + RedelegationKey = staking.RedelegationKey + RedelegationByValSrcIndexKey = staking.RedelegationByValSrcIndexKey + RedelegationByValDstIndexKey = staking.RedelegationByValDstIndexKey + UnbondingQueueKey = staking.UnbondingQueueKey + RedelegationQueueKey = staking.RedelegationQueueKey + ValidatorQueueKey = staking.ValidatorQueueKey + KeyUnbondingTime = staking.KeyUnbondingTime + KeyMaxValidators = staking.KeyMaxValidators + KeyMaxEntries = staking.KeyMaxEntries + KeyBondDenom = staking.KeyBondDenom +) + +type ( + Keeper = staking.Keeper + Commission = staking.Commission + CommissionRates = staking.CommissionRates + DVPair = staking.DVPair + DVVTriplet = staking.DVVTriplet + Delegation = staking.Delegation + Delegations = staking.Delegations + UnbondingDelegation = staking.UnbondingDelegation + UnbondingDelegationEntry = staking.UnbondingDelegationEntry + UnbondingDelegations = staking.UnbondingDelegations + Redelegation = staking.Redelegation + RedelegationEntry = staking.RedelegationEntry + Redelegations = staking.Redelegations + DelegationResponse = staking.DelegationResponse + DelegationResponses = staking.DelegationResponses + RedelegationResponse = staking.RedelegationResponse + RedelegationEntryResponse = staking.RedelegationEntryResponse + RedelegationResponses = staking.RedelegationResponses + CodeType = staking.CodeType + GenesisState = staking.GenesisState + LastValidatorPower = staking.LastValidatorPower + MultiStakingHooks = staking.MultiStakingHooks + MsgCreateValidator = staking.MsgCreateValidator + MsgEditValidator = staking.MsgEditValidator + MsgDelegate = staking.MsgDelegate + MsgBeginRedelegate = staking.MsgBeginRedelegate + MsgUndelegate = staking.MsgUndelegate + Params = staking.Params + Pool = staking.Pool + QueryDelegatorParams = staking.QueryDelegatorParams + QueryValidatorParams = staking.QueryValidatorParams + QueryBondsParams = staking.QueryBondsParams + QueryRedelegationParams = staking.QueryRedelegationParams + QueryValidatorsParams = staking.QueryValidatorsParams + Validator = staking.Validator + Validators = staking.Validators + Description = staking.Description + DelegationI = staking.DelegationI + ValidatorI = staking.ValidatorI + CosmosAppModule = staking.AppModule + CosmosAppModuleBasic = staking.AppModuleBasic +) diff --git a/x/staking/codec.go b/x/staking/internal/types/codec.go similarity index 74% rename from x/staking/codec.go rename to x/staking/internal/types/codec.go index 7e9900723..ecf3805cb 100644 --- a/x/staking/codec.go +++ b/x/staking/internal/types/codec.go @@ -1,4 +1,4 @@ -package staking +package types import ( "github.com/cosmos/cosmos-sdk/codec" @@ -14,12 +14,12 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(staking.MsgBeginRedelegate{}, "staking/MsgBeginRedelegate", nil) } -// generic sealed codec to be used throughout sdk -var MsgCdc *codec.Codec +// generic sealed codec to be used throughout this module +var ModuleCdc *codec.Codec func init() { - cdc := codec.New() - RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - MsgCdc = cdc.Seal() + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() } diff --git a/x/staking/internal/types/expected_keepers.go b/x/staking/internal/types/expected_keepers.go new file mode 100644 index 000000000..53089b450 --- /dev/null +++ b/x/staking/internal/types/expected_keepers.go @@ -0,0 +1,35 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// DistributionKeeper expected distribution keeper (noalias) +type DistributionKeeper interface { + GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins + GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.ValAddress) sdk.DecCoins +} + +// AccountKeeper defines the expected account keeper (noalias) +type AccountKeeper interface { + IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool)) +} + +// SupplyKeeper defines the expected supply Keeper (noalias) +type SupplyKeeper interface { + GetSupply(ctx sdk.Context) supplyexported.SupplyI + + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) + + SendCoinsFromModuleToModule(ctx sdk.Context, senderPool, recipientPool string, amt sdk.Coins) sdk.Error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error +} diff --git a/x/staking/module.go b/x/staking/module.go new file mode 100644 index 000000000..f8e7f664a --- /dev/null +++ b/x/staking/module.go @@ -0,0 +1,155 @@ +package staking + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + flag "github.com/spf13/pflag" + + abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/staking/internal/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + // customize to set default genesis state bond denom to luna + defaultGenesisState := DefaultGenesisState() + defaultGenesisState.Params.BondDenom = core.MicroLunaDenom + + return ModuleCdc.MustMarshalJSON(defaultGenesisState) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//_____________________________________ +// extra helpers + +// CreateValidatorMsgHelpers - used for gen-tx +func (AppModuleBasic) CreateValidatorMsgHelpers(ipDefault string) ( + fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { + return CosmosAppModuleBasic{}.CreateValidatorMsgHelpers(ipDefault) +} + +// PrepareFlagsForTxCreateValidator - used for gen-tx +func (AppModuleBasic) PrepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, + chainID string, valPubKey crypto.PubKey) { + CosmosAppModuleBasic{}.PrepareFlagsForTxCreateValidator(config, nodeID, chainID, valPubKey) +} + +// BuildCreateValidatorMsg - used for gen-tx +func (AppModuleBasic) BuildCreateValidatorMsg(cliCtx context.CLIContext, + txBldr authtypes.TxBuilder) (authtypes.TxBuilder, sdk.Msg, error) { + return CosmosAppModuleBasic{}.BuildCreateValidatorMsg(cliCtx, txBldr) +} + +//___________________________ +// app module for staking +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, distrKeeper types.DistributionKeeper, accKeeper types.AccountKeeper, + supplyKeeper types.SupplyKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper, distrKeeper, accKeeper, supplyKeeper), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/supply/alias.go b/x/supply/alias.go new file mode 100644 index 000000000..eb3c6718c --- /dev/null +++ b/x/supply/alias.go @@ -0,0 +1,17 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/supply/internal/types/ +package supply + +import ( + "github.com/terra-project/core/x/supply/internal/types" +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + + // variable aliases + ModuleCdc = types.ModuleCdc +) diff --git a/x/supply/cosmos_alias.go b/x/supply/cosmos_alias.go new file mode 100644 index 000000000..296d6951e --- /dev/null +++ b/x/supply/cosmos_alias.go @@ -0,0 +1,49 @@ +// nolint +package supply + +import ( + "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +const ( + ModuleName = supply.ModuleName + StoreKey = supply.StoreKey + RouterKey = supply.RouterKey + QuerierRoute = supply.QuerierRoute + Minter = supply.Minter + Burner = supply.Burner + Staking = supply.Staking +) + +var ( + // functions aliases + RegisterInvariants = supply.RegisterInvariants + AllInvariants = supply.AllInvariants + TotalSupply = supply.TotalSupply + NewKeeper = supply.NewKeeper + NewQuerier = supply.NewQuerier + SupplyKey = supply.SupplyKey + NewModuleAddress = supply.NewModuleAddress + NewEmptyModuleAccount = supply.NewEmptyModuleAccount + NewModuleAccount = supply.NewModuleAccount + NewGenesisState = supply.NewGenesisState + DefaultGenesisState = supply.DefaultGenesisState + NewSupply = supply.NewSupply + DefaultSupply = supply.DefaultSupply + NewCosmosAppModule = supply.NewAppModule + + // variable aliases + DefaultCodespace = supply.DefaultCodespace + CosmosModuleCdc = supply.ModuleCdc +) + +type ( + Keeper = supply.Keeper + ModuleAccount = supply.ModuleAccount + GenesisState = supply.GenesisState + Supply = supply.Supply + CosmosAppModule = supply.AppModule + CosmosAppModuleBasic = supply.AppModuleBasic + ModuleAccountI = exported.ModuleAccountI +) diff --git a/x/supply/internal/types/codec.go b/x/supply/internal/types/codec.go new file mode 100644 index 000000000..b5ff39e46 --- /dev/null +++ b/x/supply/internal/types/codec.go @@ -0,0 +1,25 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// RegisterCodec registers the account types and interface +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil) + cdc.RegisterInterface((*exported.SupplyI)(nil), nil) + cdc.RegisterConcrete(&supply.ModuleAccount{}, "supply/ModuleAccount", nil) + cdc.RegisterConcrete(&supply.Supply{}, "supply/Supply", nil) +} + +// ModuleCdc generic sealed codec to be used throughout module +var ModuleCdc *codec.Codec + +func init() { + cdc := codec.New() + RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + ModuleCdc = cdc.Seal() +} diff --git a/x/supply/internal/types/expected_keepers.go b/x/supply/internal/types/expected_keepers.go new file mode 100644 index 000000000..ab6f7b020 --- /dev/null +++ b/x/supply/internal/types/expected_keepers.go @@ -0,0 +1,14 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/exported" +) + +// AccountKeeper defines the expected account keeper (noalias) +type AccountKeeper interface { + IterateAccounts(ctx sdk.Context, process func(exported.Account) (stop bool)) + GetAccount(sdk.Context, sdk.AccAddress) exported.Account + SetAccount(sdk.Context, exported.Account) + NewAccount(sdk.Context, exported.Account) exported.Account +} diff --git a/x/supply/module.go b/x/supply/module.go new file mode 100644 index 000000000..f0faab192 --- /dev/null +++ b/x/supply/module.go @@ -0,0 +1,124 @@ +package supply + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/terra-project/core/x/supply/internal/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return CosmosAppModuleBasic{}.Name() +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) + *CosmosModuleCdc = *ModuleCdc // nolint +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return CosmosAppModuleBasic{}.DefaultGenesis() +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + return CosmosAppModuleBasic{}.ValidateGenesis(bz) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(cliCtx context.CLIContext, route *mux.Router) { + CosmosAppModuleBasic{}.RegisterRESTRoutes(cliCtx, route) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetTxCmd(cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return CosmosAppModuleBasic{}.GetQueryCmd(cdc) +} + +//___________________________ +// app module for supply +type AppModule struct { + AppModuleBasic + cosmosAppModule CosmosAppModule +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, ak types.AccountKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + cosmosAppModule: NewCosmosAppModule(keeper, ak), + } +} + +// module name +func (am AppModule) Name() string { + return am.cosmosAppModule.Name() +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + am.cosmosAppModule.RegisterInvariants(ir) +} + +// module querier route name +func (am AppModule) Route() string { + return am.cosmosAppModule.Route() +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return am.cosmosAppModule.NewHandler() +} + +// module querier route name +func (am AppModule) QuerierRoute() string { return am.cosmosAppModule.QuerierRoute() } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { return am.cosmosAppModule.NewQuerierHandler() } + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + return am.cosmosAppModule.InitGenesis(ctx, data) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return am.cosmosAppModule.ExportGenesis(ctx) +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, rbb abci.RequestBeginBlock) { + am.cosmosAppModule.BeginBlock(ctx, rbb) +} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, rbb abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.cosmosAppModule.EndBlock(ctx, rbb) +} diff --git a/x/treasury/abci.go b/x/treasury/abci.go new file mode 100644 index 000000000..faf227af0 --- /dev/null +++ b/x/treasury/abci.go @@ -0,0 +1,42 @@ +package treasury + +import ( + "github.com/terra-project/core/x/treasury/internal/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" +) + +// EndBlocker is called at the end of every block +func EndBlocker(ctx sdk.Context, k Keeper) { + + // Check epoch last block + if !core.IsPeriodLastBlock(ctx, core.BlocksPerEpoch) { + return + } + + // Update luna issuance after finish all works + defer k.UpdateIssuance(ctx) + + // Check probation period + if ctx.BlockHeight() < (core.BlocksPerEpoch * k.WindowProbation(ctx)) { + return + } + + k.SettleSeigniorage(ctx) + + // Update tax-rate and reward-weight of next epoch + taxRate := k.UpdateTaxPolicy(ctx) + rewardWeight := k.UpdateRewardPolicy(ctx) + taxCap := k.UpdateTaxCap(ctx) + + ctx.EventManager().EmitEvent( + sdk.NewEvent(types.EventTypePolichUpdate, + sdk.NewAttribute(types.AttributeKeyTax, taxRate.String()), + sdk.NewAttribute(types.AttributeKeyReward, rewardWeight.String()), + sdk.NewAttribute(types.AttributeKeyTaxCap, taxCap.String()), + ), + ) + +} diff --git a/x/treasury/abci_test.go b/x/treasury/abci_test.go new file mode 100644 index 000000000..ff0c13ee1 --- /dev/null +++ b/x/treasury/abci_test.go @@ -0,0 +1,26 @@ +package treasury + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/treasury/internal/keeper" +) + +func TestEndBlockerIssuanceUpdate(t *testing.T) { + input := keeper.CreateTestInput(t) + + targetIssuance := sdk.NewInt(1000) + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch - 1) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, targetIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + EndBlocker(input.Ctx, input.TreasuryKeeper) + + issuance := input.TreasuryKeeper.GetHistoricalIssuance(input.Ctx, 0).AmountOf(core.MicroLunaDenom) + require.Equal(t, targetIssuance, issuance) +} diff --git a/x/treasury/alias.go b/x/treasury/alias.go new file mode 100644 index 000000000..2c04e7055 --- /dev/null +++ b/x/treasury/alias.go @@ -0,0 +1,103 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/terra-project/core/x/treasury/internal/types/ +// ALIASGEN: github.com/terra-project/core/x/treasury/internal/keeper/ +package treasury + +import ( + "github.com/terra-project/core/x/treasury/internal/keeper" + "github.com/terra-project/core/x/treasury/internal/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + CodeInvalidEpoch = types.CodeInvalidEpoch + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + DefaultParamspace = types.DefaultParamspace + QueryCurrentEpoch = types.QueryCurrentEpoch + QueryTaxRate = types.QueryTaxRate + QueryTaxCap = types.QueryTaxCap + QueryRewardWeight = types.QueryRewardWeight + QuerySeigniorageProceeds = types.QuerySeigniorageProceeds + QueryTaxProceeds = types.QueryTaxProceeds + QueryParameters = types.QueryParameters + QueryHistoricalIssuance = types.QueryHistoricalIssuance +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + ErrInvalidEpoch = types.ErrInvalidEpoch + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + ValidateGenesis = types.ValidateGenesis + GetTaxRateKey = types.GetTaxRateKey + GetRewardWeightKey = types.GetRewardWeightKey + GetTaxCapKey = types.GetTaxCapKey + GetTaxProceedsKey = types.GetTaxProceedsKey + GetHistoricalIssuanceKey = types.GetHistoricalIssuanceKey + DefaultParams = types.DefaultParams + NewQueryTaxCapParams = types.NewQueryTaxCapParams + NewQueryTaxRateParams = types.NewQueryTaxRateParams + NewQueryRewardWeightParams = types.NewQueryRewardWeightParams + NewQuerySeigniorageParams = types.NewQuerySeigniorageParams + NewQueryTaxProceedsParams = types.NewQueryTaxProceedsParams + NewQueryHistoricalIssuanceParams = types.NewQueryHistoricalIssuanceParams + TaxRewardsForEpoch = keeper.TaxRewardsForEpoch + SeigniorageRewardsForEpoch = keeper.SeigniorageRewardsForEpoch + MiningRewardForEpoch = keeper.MiningRewardForEpoch + TRL = keeper.TRL + SRL = keeper.SRL + MRL = keeper.MRL + UnitLunaIndicator = keeper.UnitLunaIndicator + SumIndicator = keeper.SumIndicator + RollingAverageIndicator = keeper.RollingAverageIndicator + NewKeeper = keeper.NewKeeper + ParamKeyTable = keeper.ParamKeyTable + NewQuerier = keeper.NewQuerier + + // variable aliases + ModuleCdc = types.ModuleCdc + TaxRateKey = types.TaxRateKey + RewardWeightKey = types.RewardWeightKey + TaxCapKey = types.TaxCapKey + TaxProceedsKey = types.TaxProceedsKey + HistoricalIssuanceKey = types.HistoricalIssuanceKey + ParamStoreKeyTaxPolicy = types.ParamStoreKeyTaxPolicy + ParamStoreKeyRewardPolicy = types.ParamStoreKeyRewardPolicy + ParamStoreKeySeigniorageBurdenTarget = types.ParamStoreKeySeigniorageBurdenTarget + ParamStoreKeyMiningIncrement = types.ParamStoreKeyMiningIncrement + ParamStoreKeyWindowShort = types.ParamStoreKeyWindowShort + ParamStoreKeyWindowLong = types.ParamStoreKeyWindowLong + ParamStoreKeyWindowProbation = types.ParamStoreKeyWindowProbation + DefaultTaxPolicy = types.DefaultTaxPolicy + DefaultRewardPolicy = types.DefaultRewardPolicy + DefaultSeigniorageBurdenTarget = types.DefaultSeigniorageBurdenTarget + DefaultMiningIncrement = types.DefaultMiningIncrement + DefaultWindowShort = types.DefaultWindowShort + DefaultWindowLong = types.DefaultWindowLong + DefaultWindowProbation = types.DefaultWindowProbation + DefaultTaxRate = types.DefaultTaxRate + DefaultRewardWeight = types.DefaultRewardWeight +) + +type ( + PolicyConstraints = types.PolicyConstraints + SupplyKeeper = types.SupplyKeeper + MarketKeeper = types.MarketKeeper + StakingKeeper = types.StakingKeeper + DistributionKeeper = types.DistributionKeeper + GenesisState = types.GenesisState + Params = types.Params + QueryTaxCapParams = types.QueryTaxCapParams + QueryTaxRateParams = types.QueryTaxRateParams + QueryRewardWeightParams = types.QueryRewardWeightParams + QuerySeigniorageProceedsParams = types.QuerySeigniorageProceedsParams + QueryTaxProceedsParams = types.QueryTaxProceedsParams + QueryHistoricalIssuanceParams = types.QueryHistoricalIssuanceParams + Keeper = keeper.Keeper +) diff --git a/x/treasury/client/cli/cli_test.go b/x/treasury/client/cli/cli_test.go deleted file mode 100644 index 951bc215a..000000000 --- a/x/treasury/client/cli/cli_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/spf13/cobra" - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/testutil" - "github.com/terra-project/core/x/treasury" -) - -func TestQueryTaxRate(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryTaxRate := GetCmdQueryTaxRate(cdc) - - // Name check - require.Equal(t, treasury.QueryTaxRate, queryTaxRate.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryTaxRate.Args)) - - // Check Flags - epochFlag := queryTaxRate.Flag(flagEpoch) - require.NotNil(t, epochFlag) -} - -func TestQueryTaxCap(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryTaxCap := GetCmdQueryTaxCap(cdc) - - // Name check - require.Equal(t, treasury.QueryTaxCap, queryTaxCap.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryTaxCap.Args)) - - // Check Flags - denomFlag := queryTaxCap.Flag(flagDenom) - require.NotNil(t, denomFlag) - require.Equal(t, []string{"true"}, denomFlag.Annotations[cobra.BashCompOneRequiredFlag]) -} - -func TestQueryIssuance(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryIssuance := GetCmdQueryIssuance(cdc) - - // Name check - require.Equal(t, treasury.QueryIssuance, queryIssuance.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryIssuance.Args)) - - // Check Flags - denomFlag := queryIssuance.Flag(flagDenom) - require.NotNil(t, denomFlag) - require.Equal(t, []string{"true"}, denomFlag.Annotations[cobra.BashCompOneRequiredFlag]) - - dayFlag := queryIssuance.Flag(flagDenom) - require.NotNil(t, dayFlag) -} - -func TestQueryMiningRewardWeight(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryMiningRewardWeight := GetCmdQueryMiningRewardWeight(cdc) - - // Name check - require.Equal(t, treasury.QueryMiningRewardWeight, queryMiningRewardWeight.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryMiningRewardWeight.Args)) - - // Check Flags - epochFlag := queryMiningRewardWeight.Flag(flagEpoch) - require.NotNil(t, epochFlag) -} - -func TestQueryTaxProceeds(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryTaxProceeds := GetCmdQueryTaxProceeds(cdc) - - // Name check - require.Equal(t, treasury.QueryTaxProceeds, queryTaxProceeds.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryTaxProceeds.Args)) - - // Check Flags - epochFlag := queryTaxProceeds.Flag(flagEpoch) - require.NotNil(t, epochFlag) -} - -func TestQuerySeigniorageProceeds(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - querySeigniorageProceeds := GetCmdQuerySeigniorageProceeds(cdc) - - // Name check - require.Equal(t, treasury.QuerySeigniorageProceeds, querySeigniorageProceeds.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(querySeigniorageProceeds.Args)) - - // Check Flags - epochFlag := querySeigniorageProceeds.Flag(flagEpoch) - require.NotNil(t, epochFlag) -} - -func TestQueryCurrentEpoch(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryCurrentEpoch := GetCmdQueryCurrentEpoch(cdc) - - // Name check - require.Equal(t, treasury.QueryCurrentEpoch, queryCurrentEpoch.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryCurrentEpoch.Args)) -} - -func TestQueryParams(t *testing.T) { - cdc, _, _, _ := testutil.PrepareCmdTest() - - queryParams := GetCmdQueryParams(cdc) - - // Name check - require.Equal(t, treasury.QueryParams, queryParams.Name()) - - // NoArg check - require.Equal(t, testutil.FS(cobra.PositionalArgs(cobra.NoArgs)), testutil.FS(queryParams.Args)) -} diff --git a/x/treasury/client/cli/query.go b/x/treasury/client/cli/query.go index 8d97d890b..9577f1fb3 100644 --- a/x/treasury/client/cli/query.go +++ b/x/treasury/client/cli/query.go @@ -1,15 +1,16 @@ package cli import ( + "errors" "fmt" "strconv" "strings" - "github.com/terra-project/core/x/treasury" + "github.com/terra-project/core/x/treasury/internal/types" "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,227 +18,248 @@ import ( const ( flagDenom = "denom" - flagDay = "day" flagEpoch = "epoch" ) +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(cdc *codec.Codec) *cobra.Command { + oracleQueryCmd := &cobra.Command{ + Use: "treasury", + Short: "Querying commands for the treasury module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + oracleQueryCmd.AddCommand(client.GetCommands( + GetCmdQueryTaxRate(cdc), + GetCmdQueryTaxCap(cdc), + GetCmdQueryHistoricalIssuance(cdc), + GetCmdQueryRewardWeight(cdc), + GetCmdQueryParams(cdc), + GetCmdQueryTaxProceeds(cdc), + GetCmdQuerySeigniorageProceeds(cdc), + GetCmdQueryCurrentEpoch(cdc), + GetCmdQueryParams(cdc), + )...) + + return oracleQueryCmd + +} + // GetCmdQueryTaxRate implements the query taxrate command. func GetCmdQueryTaxRate(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryTaxRate, - Args: cobra.NoArgs, + Use: "tax-rate [epoch]", + Args: cobra.RangeArgs(0, 1), Short: "Query the stability tax rate", Long: strings.TrimSpace(` Query the stability tax rate at the specified epoch. -$ terracli query treasury tax-rate --epoch=14 +$ terracli query treasury tax-rate 14 `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - var epoch sdk.Int - epochStr := viper.GetString(flagEpoch) - if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + var epoch int64 + if len(args) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cdc.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - return fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) + var err error + epoch, err = strconv.ParseInt(args[0], 10, 64) + if err != nil { + return errors.New(sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxRate, epoch.String()), nil) + params := types.NewQueryTaxRateParams(epoch) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxRate), bz) if err != nil { return err } - var taxRate treasury.QueryTaxRateResponse + var taxRate sdk.Dec cdc.MustUnmarshalJSON(res, &taxRate) return cliCtx.PrintOutput(taxRate) }, } - cmd.Flags().String(flagEpoch, "", "(optional) an epoch number which you wants to get tax rate of; default is current epoch") return cmd } // GetCmdQueryTaxCap implements the query taxcap command. func GetCmdQueryTaxCap(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryTaxCap, - Args: cobra.NoArgs, + Use: "tax-cap [denom]", + Args: cobra.ExactArgs(1), Short: "Query the current stability tax cap of a denom asset", Long: strings.TrimSpace(` Query the current stability tax cap of the denom asset. The stability tax levied on a tx is at most tax cap, regardless of the size of the transaction. -$ terracli query treasury tax-cap --denom="ukrw" +$ terracli query treasury tax-cap ukrw `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - denom := viper.GetString(flagDenom) + denom := args[0] - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxCap, denom), nil) + params := types.NewQueryTaxCapParams(denom) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxCap), bz) if err != nil { return err } - var taxCap treasury.QueryTaxCapResponse + var taxCap sdk.Dec cdc.MustUnmarshalJSON(res, &taxCap) return cliCtx.PrintOutput(taxCap) }, } - cmd.Flags().String(flagDenom, "", "the denom for which you want to know the taxcap of") - - cmd.MarkFlagRequired(flagDenom) - return cmd } -// GetCmdQueryIssuance implements the query issuance command. -func GetCmdQueryIssuance(cdc *codec.Codec) *cobra.Command { +// GetCmdQueryHistoricalIssuance implements the query issuance command. +func GetCmdQueryHistoricalIssuance(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryIssuance, - Args: cobra.NoArgs, - Short: "Query the current issuance of a denom asset", + Use: "historical-issuance [epoch]", + Args: cobra.RangeArgs(0, 1), + Short: "Query the epoch historical issuance", Long: strings.TrimSpace(` -Query the current issuance of a denom asset. +Query the epoch issuance -$ terracli query treasury issuance --denom="ukrw --day 0" +$ terracli query treasury historical-issuance 0" `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - denom := viper.GetString(flagDenom) - dayStr := viper.GetString(flagDay) - - if len(dayStr) != 0 { - _, err := strconv.ParseInt(dayStr, 10, 64) + var epoch int64 + if len(args) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } + + cdc.MustUnmarshalJSON(res, &epoch) + } else { + var err error + epoch, err = strconv.ParseInt(args[0], 10, 64) + if err != nil { + return errors.New(sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) + } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s/%s", treasury.QuerierRoute, treasury.QueryIssuance, denom, dayStr), nil) + params := types.NewQueryHistoricalIssuanceParams(epoch) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalIssuance), bz) if err != nil { return err } - var issuance treasury.QueryIssuanceResponse + var issuance sdk.Coins cdc.MustUnmarshalJSON(res, &issuance) return cliCtx.PrintOutput(issuance) }, } - cmd.Flags().String(flagDenom, "", "the denom which you want to know the issueance of") - cmd.Flags().String(flagDay, "", "the # of date after genesis time, a user want to query") - - cmd.MarkFlagRequired(flagDenom) - return cmd } -// GetCmdQueryMiningRewardWeight implements the query reward-weight command. -func GetCmdQueryMiningRewardWeight(cdc *codec.Codec) *cobra.Command { +// GetCmdQueryRewardWeight implements the query reward-weight command. +func GetCmdQueryRewardWeight(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryMiningRewardWeight, - Args: cobra.NoArgs, - Short: "Query the mining reward weight", + Use: "reward-weight [epoch]", + Args: cobra.RangeArgs(0, 1), + Short: "Query the reward weight", Long: strings.TrimSpace(` -Query the mining reward rate at the specified epoch. +Query the reward rate at the specified epoch. -$ terracli query treasury reward-weight --epoch=14 +$ terracli query treasury reward-weight 14 `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - var epoch sdk.Int - epochStr := viper.GetString(flagEpoch) - if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + var epoch int64 + if len(args) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cdc.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - return fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) + var err error + epoch, err = strconv.ParseInt(args[0], 10, 64) + if err != nil { + return errors.New(sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryMiningRewardWeight, epoch.String()), nil) + params := types.NewQueryRewardWeightParams(epoch) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryRewardWeight), bz) if err != nil { return err } - var rewardWeight treasury.QueryMiningRewardWeightResponse + var rewardWeight sdk.Dec cdc.MustUnmarshalJSON(res, &rewardWeight) return cliCtx.PrintOutput(rewardWeight) }, } - cmd.Flags().String(flagEpoch, "", "(optional) an epoch number which you wants to get reward weight of; default is current epoch") - return cmd } // GetCmdQueryTaxProceeds implements the query tax-proceeds command. func GetCmdQueryTaxProceeds(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryTaxProceeds, - Args: cobra.NoArgs, + Use: "tax-proceeds [epoch]", + Args: cobra.RangeArgs(0, 1), Short: "Query the tax proceeds for the epoch", Long: strings.TrimSpace(` Query the tax proceeds corresponding to the given epoch. The return value will be sdk.Coins{} of all the taxes collected. -$ terracli query treasury tax-proceeds --epoch=14 +$ terracli query treasury tax-proceeds 14 `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - var epoch sdk.Int - epochStr := viper.GetString(flagEpoch) - if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + var epoch int64 + if len(args) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cdc.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - return fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) + var err error + epoch, err = strconv.ParseInt(args[0], 10, 64) + if err != nil { + return errors.New(sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxProceeds, epoch.String()), nil) + params := types.NewQueryTaxProceedsParams(epoch) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxProceeds), bz) if err != nil { return err } - var taxProceeds treasury.QueryTaxProceedsResponse + var taxProceeds sdk.Coins cdc.MustUnmarshalJSON(res, &taxProceeds) return cliCtx.PrintOutput(taxProceeds) }, @@ -251,57 +273,54 @@ $ terracli query treasury tax-proceeds --epoch=14 // GetCmdQuerySeigniorageProceeds implements the query seigniorage-proceeds command. func GetCmdQuerySeigniorageProceeds(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QuerySeigniorageProceeds, - Args: cobra.NoArgs, + Use: "seigniorage-proceeds [epoch]", + Args: cobra.RangeArgs(0, 1), Short: "Query the seigniorage proceeds for the epoch", Long: strings.TrimSpace(` Query the seigniorage proceeds corresponding to the given epoch. The return value will be in units of 'uluna' coins. -$ terracli query treasury seigniorage-proceeds --epoch=14 +$ terracli query treasury seigniorage-proceeds 14 `), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - var epoch sdk.Int - epochStr := viper.GetString(flagEpoch) - if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + var epoch int64 + if len(args) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cdc.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - return fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) + var err error + epoch, err = strconv.ParseInt(args[0], 10, 64) + if err != nil { + return errors.New(sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QuerySeigniorageProceeds, epoch.String()), nil) + params := types.NewQuerySeigniorageParams(epoch) + bz := cdc.MustMarshalJSON(params) + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySeigniorageProceeds), bz) if err != nil { return err } - var seigniorageProceeds treasury.QuerySeigniorageProceedsResponse + var seigniorageProceeds sdk.Int cdc.MustUnmarshalJSON(res, &seigniorageProceeds) return cliCtx.PrintOutput(seigniorageProceeds) }, } - cmd.Flags().String(flagEpoch, "", "(optional) an epoch number which you wants to get seigniorage proceeds of; default is current epoch") - return cmd } // GetCmdQueryCurrentEpoch implements the query seigniorage-proceeds command. func GetCmdQueryCurrentEpoch(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryCurrentEpoch, + Use: "current-epoch", Args: cobra.NoArgs, Short: "Query the current epoch number", Long: strings.TrimSpace(` @@ -312,14 +331,14 @@ $ terracli query treasury current-epoch RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { return err } - var curEpoch treasury.QueryCurrentEpochResponse + var curEpoch int64 cdc.MustUnmarshalJSON(res, &curEpoch) - return cliCtx.PrintOutput(curEpoch) + return cliCtx.PrintOutput(sdk.NewInt(curEpoch)) }, } @@ -329,18 +348,18 @@ $ terracli query treasury current-epoch // GetCmdQueryParams implements the query params command. func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: treasury.QueryParams, + Use: "params", Args: cobra.NoArgs, - Short: "Query the current Treasury params", + Short: "Query the current Treasury parameters", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryParams), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) if err != nil { return err } - var params treasury.Params + var params types.Params cdc.MustUnmarshalJSON(res, ¶ms) return cliCtx.PrintOutput(params) }, diff --git a/x/treasury/client/module_client.go b/x/treasury/client/module_client.go deleted file mode 100644 index 9613b2927..000000000 --- a/x/treasury/client/module_client.go +++ /dev/null @@ -1,47 +0,0 @@ -package client - -import ( - "github.com/spf13/cobra" - "github.com/tendermint/go-amino" - - treasuryCli "github.com/terra-project/core/x/treasury/client/cli" - - "github.com/cosmos/cosmos-sdk/client" -) - -// ModuleClient exports all client functionality from this module -type ModuleClient struct { - storeKey string - cdc *amino.Codec -} - -func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { - return ModuleClient{storeKey, cdc} -} - -// GetQueryCmd returns the cli query commands for this module -func (mc ModuleClient) GetQueryCmd() *cobra.Command { - // Group treasury queries under a subcommand - treasuryQueryCmd := &cobra.Command{ - Use: "treasury", - Short: "Querying commands for the treasury module", - } - - treasuryQueryCmd.AddCommand(client.GetCommands( - treasuryCli.GetCmdQueryTaxRate(mc.cdc), - treasuryCli.GetCmdQueryTaxCap(mc.cdc), - treasuryCli.GetCmdQueryMiningRewardWeight(mc.cdc), - treasuryCli.GetCmdQueryIssuance(mc.cdc), - treasuryCli.GetCmdQueryTaxProceeds(mc.cdc), - treasuryCli.GetCmdQuerySeigniorageProceeds(mc.cdc), - treasuryCli.GetCmdQueryCurrentEpoch(mc.cdc), - treasuryCli.GetCmdQueryParams(mc.cdc), - )...) - - return treasuryQueryCmd -} - -// GetTxCmd The treasury module returns no TX commands. -func (mc ModuleClient) GetTxCmd() *cobra.Command { - return &cobra.Command{Hidden: true} -} diff --git a/x/treasury/client/module_client_test.go b/x/treasury/client/module_client_test.go deleted file mode 100644 index 953e8ab83..000000000 --- a/x/treasury/client/module_client_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/terra-project/core/app" -) - -const ( - storeKey = string("budget") -) - -var ( - queryCmdList = map[string]bool{ - "params": true, - "tax-rate": true, - "tax-cap": true, - "reward-weight": true, - "seigniorage-proceeds": true, - "current-epoch": true, - "issuance": true, - "tax-proceeds": true, - } -) - -func TestQueryCmdInvariant(t *testing.T) { - - cdc := app.MakeCodec() - mc := NewModuleClient(storeKey, cdc) - - for _, cmd := range mc.GetQueryCmd().Commands() { - _, ok := queryCmdList[cmd.Name()] - require.True(t, ok) - } - - require.Equal(t, len(queryCmdList), len(mc.GetQueryCmd().Commands())) -} diff --git a/x/treasury/client/rest/query.go b/x/treasury/client/rest/query.go index ce8f7c724..e9c3fd4c5 100644 --- a/x/treasury/client/rest/query.go +++ b/x/treasury/client/rest/query.go @@ -5,242 +5,293 @@ import ( "net/http" "strconv" - "github.com/terra-project/core/x/treasury" + "github.com/terra-project/core/x/treasury/internal/types" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/gorilla/mux" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QueryTaxRate), queryTaxRateHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QueryTaxRate, RestEpoch), queryTaxRateHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QueryTaxCap, RestDenom), queryTaxCapHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QueryMiningRewardWeight), queryMiningWeightHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QueryMiningRewardWeight, RestDenom), queryMiningWeightHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QueryIssuance, RestDenom), queryIssuanceHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}/{%s}", treasury.QueryIssuance, RestDenom, RestDay), queryIssuanceHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QueryTaxProceeds), queryTaxProceedsHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QueryTaxProceeds, RestEpoch), queryTaxProceedsHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QuerySeigniorageProceeds), querySgProceedsHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s/{%s}", treasury.QuerySeigniorageProceeds, RestEpoch), querySgProceedsHandlerFunction(cdc, cliCtx)).Methods("GET") - - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QueryCurrentEpoch), queryCurrentEpochHandlerFunction(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/treasury/%s", treasury.QueryParams), queryParamsHandlerFn(cdc, cliCtx)).Methods("GET") +func registerQueryRoute(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc("/treasury/tax_rate", queryTaxRateHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/tax_rate/{%s}", RestEpoch), queryTaxRateHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/tax_cap/{%s}", RestDenom), queryTaxCapHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/reward_weight", queryRewardWeightHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/reward_weight/{%s}", RestEpoch), queryRewardWeightHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/historical_issuance", queryHistoricalIssuanceHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/historical_issuance/{%s}", RestEpoch), queryHistoricalIssuanceHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/tax_proceeds", queryTaxProceedsHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/tax_proceeds/{%s}", RestEpoch), queryTaxProceedsHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/seigniorage_proceeds", querySeigniorageProceedsHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/treasury/seigniorage_proceeds/{%s}", RestEpoch), querySeigniorageProceedsHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/current_epoch", queryCurrentEpochHandlerFunction(cliCtx)).Methods("GET") + r.HandleFunc("/treasury/parameters", queryParametersHandlerFn(cliCtx)).Methods("GET") } -func queryTaxRateHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryTaxRateHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) epochStr := vars[RestEpoch] - var epoch sdk.Int + var epoch int64 if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - err := fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + var err error + epoch, err = strconv.ParseInt(epochStr, 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) return } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxRate, epoch), nil) + params := types.NewQueryTaxRateParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxRate), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryTaxCapHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryTaxCapHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) denom := vars[RestDenom] - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxCap, denom), nil) + params := types.NewQueryTaxCapParams(denom) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxCap), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryMiningWeightHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryRewardWeightHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) epochStr := vars[RestEpoch] - var epoch sdk.Int + var epoch int64 if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) - - epoch = epochResponse.CurrentEpoch + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - err := fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + var err error + epoch, err = strconv.ParseInt(epochStr, 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) return } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryMiningRewardWeight, epoch), nil) + params := types.NewQueryRewardWeightParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryRewardWeight), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryIssuanceHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryHistoricalIssuanceHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) - denom := vars[RestDenom] - dayStr := vars[RestDay] + epochStr := vars[RestEpoch] + + var epoch int64 + if len(epochStr) == 0 { + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } - if len(dayStr) != 0 { - _, err := strconv.ParseInt(dayStr, 10, 64) + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) + } else { + var err error + epoch, err = strconv.ParseInt(epochStr, 10, 64) if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) return } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s/%s", treasury.QuerierRoute, treasury.QueryIssuance, denom, dayStr), nil) + params := types.NewQueryHistoricalIssuanceParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalIssuance), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryTaxProceedsHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryTaxProceedsHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + vars := mux.Vars(r) epochStr := vars[RestEpoch] - var epoch sdk.Int + var epoch int64 if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) - epoch = epochResponse.CurrentEpoch } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - err := fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + var err error + epoch, err = strconv.ParseInt(epochStr, 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) return } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QueryTaxProceeds, epoch), nil) + params := types.NewQueryTaxProceedsParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTaxProceeds), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func querySgProceedsHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func querySeigniorageProceedsHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) epochStr := vars[RestEpoch] - var epoch sdk.Int + var epoch int64 if len(epochStr) == 0 { - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - var epochResponse treasury.QueryCurrentEpochResponse - cdc.MustUnmarshalJSON(res, &epochResponse) + cliCtx.Codec.MustUnmarshalJSON(res, &epoch) - epoch = epochResponse.CurrentEpoch } else { - var ok bool - epoch, ok = sdk.NewIntFromString(epochStr) - if !ok { - err := fmt.Errorf("the given epoch {%s} is not a valid format; epoch should be formatted as an integer", epochStr) - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + var err error + epoch, err = strconv.ParseInt(epochStr, 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("Falied to parse epoch", err.Error())) return } } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", treasury.QuerierRoute, treasury.QuerySeigniorageProceeds, epoch), nil) + params := types.NewQuerySeigniorageParams(epoch) + bz := cliCtx.Codec.MustMarshalJSON(params) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySeigniorageProceeds), bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryCurrentEpochHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryCurrentEpochHandlerFunction(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryCurrentEpoch), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCurrentEpoch), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } -func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func queryParametersHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", treasury.QuerierRoute, treasury.QueryParams), nil) + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) } } diff --git a/x/treasury/client/rest/rest.go b/x/treasury/client/rest/rest.go index 59a7cb2bc..5816bc86a 100644 --- a/x/treasury/client/rest/rest.go +++ b/x/treasury/client/rest/rest.go @@ -1,21 +1,18 @@ package rest import ( - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" ) -// REST Variable names -// nolint const ( RestDenom = "denom" - RestDay = "day" RestEpoch = "epoch" ) -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - registerQueryRoutes(cliCtx, r, cdc) +// RegisterRoutes registers oracle-related REST handlers to a router +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + // resgisterTxRoute(cliCtx, r) + registerQueryRoute(cliCtx, r) } diff --git a/x/treasury/codec.go b/x/treasury/codec.go deleted file mode 100644 index c208cf18f..000000000 --- a/x/treasury/codec.go +++ /dev/null @@ -1,18 +0,0 @@ -package treasury - -import ( - "github.com/terra-project/core/types" - - "github.com/cosmos/cosmos-sdk/codec" -) - -var cdc = codec.New() - -// RegisterCodec registers concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(&types.Claim{}, "treasury/Claim", nil) -} - -func init() { - RegisterCodec(cdc) -} diff --git a/x/treasury/constants.go b/x/treasury/constants.go deleted file mode 100644 index e198c0dcb..000000000 --- a/x/treasury/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package treasury - -const ( - // ModuleName is the name of the treasury module - ModuleName = "treasury" - - // StoreKey is the string store representation - StoreKey = ModuleName - - // RouterKey is the msg router key for the treasury module - RouterKey = ModuleName - - // QuerierRoute is the query router key for the treasury module - QuerierRoute = ModuleName - - // DefaultParamspace is the paramspace representation - DefaultParamspace = ModuleName -) diff --git a/x/treasury/constraint_test.go b/x/treasury/constraint_test.go deleted file mode 100644 index 7b761eee1..000000000 --- a/x/treasury/constraint_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package treasury - -import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" -) - -func TestConstraintClamp(t *testing.T) { - input := createTestInput(t) - - taxPolicy := input.treasuryKeeper.GetParams(input.ctx).TaxPolicy - prevRate := input.treasuryKeeper.GetTaxRate(input.ctx, util.GetEpoch(input.ctx)) - - // Case 1: try to update delta > maxUpdateRate - newRate := prevRate.Add(taxPolicy.ChangeRateMax.MulInt64(2)) - clampedRate := taxPolicy.Clamp(prevRate, newRate) - require.Equal(t, prevRate.Add(taxPolicy.ChangeRateMax), clampedRate) - - // Case 2: try to update delta > maxUpdateRate in other direction - newRate = prevRate.Sub(taxPolicy.ChangeRateMax.MulInt64(2)) - clampedRate = taxPolicy.Clamp(prevRate, newRate) - require.Equal(t, prevRate.Sub(taxPolicy.ChangeRateMax), clampedRate) - - // Case 3: try to update the new rate > maxRate - prevRate = taxPolicy.RateMax - newRate = taxPolicy.RateMax.Add(sdk.NewDecWithPrec(1, 3)) - clampedRate = taxPolicy.Clamp(prevRate, newRate) - require.Equal(t, taxPolicy.RateMax, clampedRate) - - // Case 4: try to update the new rate < minRate - prevRate = taxPolicy.RateMin - newRate = taxPolicy.RateMin.Sub(sdk.NewDecWithPrec(1, 3)) - clampedRate = taxPolicy.Clamp(prevRate, newRate) - require.Equal(t, taxPolicy.RateMin, clampedRate) -} - -func TestConstraintCap(t *testing.T) { - input := createTestInput(t) - taxPolicy := input.treasuryKeeper.GetParams(input.ctx).TaxPolicy - - // Set prices for test assets first - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(10)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, sdk.NewDec(1000)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, sdk.NewDec(1)) - - // Check that SDR tax cap has been set - require.Equal(t, taxPolicy.Cap.Amount, input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroSDRDenom)) - require.Equal(t, sdk.NewInt(100).MulRaw(assets.MicroUnit), input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroKRWDenom)) - require.Equal(t, sdk.NewDecFromIntWithPrec(sdk.OneInt(), 1).MulInt64(assets.MicroUnit).TruncateInt(), input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroGBPDenom)) -} diff --git a/x/treasury/end_blocker.go b/x/treasury/end_blocker.go deleted file mode 100644 index 3418bc0cf..000000000 --- a/x/treasury/end_blocker.go +++ /dev/null @@ -1,39 +0,0 @@ -package treasury - -import ( - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/treasury/tags" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// not enough data collected to update variables -func isProbationPeriod(ctx sdk.Context, k Keeper) bool { - - // Look 1 block into the future ... at the last block of the epoch, trigger - futureCtx := ctx.WithBlockHeight(ctx.BlockHeight() + 1) - futureEpoch := util.GetEpoch(futureCtx) - - return futureEpoch.LT(k.GetParams(ctx).WindowProbation) -} - -// EndBlocker called to adjust macro weights (tax, mining reward) and settle outstanding claims. -func EndBlocker(ctx sdk.Context, k Keeper) (resTags sdk.Tags) { - if !util.IsPeriodLastBlock(ctx, util.BlocksPerEpoch) { - return resTags - } - - if isProbationPeriod(ctx, k) { - return resTags - } - - // Update policy weights - taxRate := updateTaxPolicy(ctx, k) - rewardWeight := updateRewardPolicy(ctx, k) - - return sdk.NewTags( - tags.Action, tags.ActionPolicyUpdate, - tags.Tax, taxRate.String(), - tags.MinerReward, rewardWeight.String(), - ) -} diff --git a/x/treasury/end_blocker_test.go b/x/treasury/end_blocker_test.go deleted file mode 100644 index cde875a57..000000000 --- a/x/treasury/end_blocker_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package treasury - -import ( - "math/rand" - "testing" - "time" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - "github.com/terra-project/core/x/treasury/tags" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" -) - -func TestEndBlockerTiming(t *testing.T) { - input := createTestInput(t) - input = reset(input) - - params := input.treasuryKeeper.GetParams(input.ctx) - - // After probationary period, we should also be updating policy variables - for i := params.WindowProbation.Int64(); i < params.WindowProbation.Int64()+12; i++ { - if i%params.WindowShort.Int64() == 0 { - input.ctx = input.ctx.WithBlockHeight(i*util.BlocksPerEpoch - 1) - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt)) - - tTags := EndBlocker(input.ctx, input.treasuryKeeper) - - require.Equal(t, tTags.ToKVPairs()[0].GetValue(), []byte(tags.ActionPolicyUpdate)) - } - } -} - -func reset(input testInput) testInput { - - // Set blocknum back to 0 - input.ctx = input.ctx.WithBlockHeight(0) - - // Reset oracle price - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(1)) - - // Reset genesis - InitGenesis(input.ctx, input.treasuryKeeper, DefaultGenesisState()) - - // Give everyone some luna - for _, addr := range addrs { - err := input.mintKeeper.Mint(input.ctx, addr, sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt)) - if err != nil { - panic(err) - } - } - - return input -} - -// updatePolicy updates -func updatePolicy(input testInput, startIndex int, - taxRevenues, seigniorageRevenues []sdk.Int) (taxRate, rewardWeight sdk.Dec, err sdk.Error, ctx sdk.Context) { - - if len(taxRevenues) != len(seigniorageRevenues) { - err = sdk.ErrInternal("lengths of inputs should be the same") - } - - params := input.treasuryKeeper.GetParams(input.ctx) - blocksPerEpoch := util.BlocksPerEpoch - - for i := 0; i < len(taxRevenues); i++ { - input.ctx = input.ctx.WithBlockHeight(params.WindowShort.Int64() * int64(i+startIndex) * blocksPerEpoch) - - taxRevenue := taxRevenues[i] - input.treasuryKeeper.RecordTaxProceeds(input.ctx, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, taxRevenue)}) - - seigniorageRevenue := seigniorageRevenues[i] - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, seigniorageRevenue)) - - // Call endblocker - EndBlocker(input.ctx, input.treasuryKeeper) - } - - taxRate = input.treasuryKeeper.GetTaxRate(input.ctx, util.GetEpoch(input.ctx)) - rewardWeight = input.treasuryKeeper.GetRewardWeight(input.ctx, util.GetEpoch(input.ctx)) - ctx = input.ctx - - return -} - -func generateRandomMacroInputs() (taxRevenues, seigniorageRevenues []sdk.Int) { - rand.Seed(int64(time.Now().Nanosecond())) - - taxRevenues = []sdk.Int{} - seigniorageRevenues = []sdk.Int{} - - numPeriods := rand.Int63() % 30 // bound to less than 30 periods - for i := 0; i < int(numPeriods); i++ { - taxRevenues = append(taxRevenues, sdk.NewInt(rand.Int63())) - seigniorageRevenues = append(seigniorageRevenues, sdk.NewInt(rand.Int63())) - } - - return -} - -func TestEndBlockerUpdatePolicy(t *testing.T) { - input := createTestInput(t) - input = reset(input) - - taxRevenues, seigniorageRevenues := generateRandomMacroInputs() - newTaxRate, newSeigniorageWeight, err, ctx := updatePolicy(input, 1, taxRevenues, seigniorageRevenues) - require.Nil(t, err) - - input.ctx = ctx - taxRate := input.treasuryKeeper.GetTaxRate(input.ctx, util.GetEpoch(input.ctx)) - rewardWeight := input.treasuryKeeper.GetRewardWeight(input.ctx, util.GetEpoch(input.ctx)) - - require.Equal(t, taxRate, newTaxRate) - require.Equal(t, rewardWeight, newSeigniorageWeight) -} diff --git a/x/treasury/expected_keepers.go b/x/treasury/expected_keepers.go deleted file mode 100644 index 9053bd58e..000000000 --- a/x/treasury/expected_keepers.go +++ /dev/null @@ -1,26 +0,0 @@ -package treasury - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// expected mint keeper -type MintKeeper interface { - PeekEpochSeigniorage(ctx sdk.Context, epoch sdk.Int) (seignioragePool sdk.Int) - Mint(ctx sdk.Context, recipient sdk.AccAddress, coin sdk.Coin) (err sdk.Error) - GetIssuance(ctx sdk.Context, denom string, day sdk.Int) (issuance sdk.Int) -} - -// expected market keeper -type MarketKeeper interface { - GetSwapDecCoin(ctx sdk.Context, offerCoin sdk.DecCoin, askDenom string) (sdk.DecCoin, sdk.Error) - GetSwapCoin(ctx sdk.Context, offerCoin sdk.Coin, askDenom string, isInternal bool) (sdk.Coin, sdk.Dec, sdk.Error) -} - -// expected coin keeper -type DistributionKeeper interface { - AllocateTokensToValidator(ctx sdk.Context, val sdk.Validator, tokens sdk.DecCoins) -} - -// expected fee keeper -type FeeCollectionKeeper interface { - AddCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins -} diff --git a/x/treasury/genesis.go b/x/treasury/genesis.go index c63f13aef..f5ec4ad9a 100644 --- a/x/treasury/genesis.go +++ b/x/treasury/genesis.go @@ -1,71 +1,28 @@ package treasury import ( - "fmt" - - "github.com/terra-project/core/types/util" - sdk "github.com/cosmos/cosmos-sdk/types" + core "github.com/terra-project/core/types" ) -// GenesisState - all treasury state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params"` // treasury params - GenesisTaxRate sdk.Dec `json:"tax_rate"` - GenesisRewardWeight sdk.Dec `json:"reward_weight"` -} - -// NewGenesisState constructs a new genesis state -func NewGenesisState(params Params, taxRate, rewardWeight sdk.Dec) GenesisState { - return GenesisState{ - Params: params, - GenesisTaxRate: taxRate, - GenesisRewardWeight: rewardWeight, - } -} - -// DefaultGenesisState returns raw genesis message for testing -func DefaultGenesisState() GenesisState { - params := DefaultParams() - return GenesisState{ - Params: params, - GenesisTaxRate: sdk.NewDecWithPrec(1, 3), // 0.1% - GenesisRewardWeight: sdk.NewDecWithPrec(5, 2), // 5% - } -} - -// InitGenesis new treasury genesis +// InitGenesis initialize default parameters +// and the keeper's address to pubkey map func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { keeper.SetParams(ctx, data.Params) - keeper.SetTaxRate(ctx, data.GenesisTaxRate) - keeper.setTaxCap(ctx, data.Params.TaxPolicy.Cap.Denom, data.Params.TaxPolicy.Cap.Amount) - keeper.SetRewardWeight(ctx, data.GenesisRewardWeight) -} + keeper.SetTaxRate(ctx, data.TaxRate) + keeper.SetRewardWeight(ctx, data.RewardWeight) -// ExportGenesis returns a GenesisState for a given context and keeper. The -// GenesisState will contain the pool, and validator/delegator distribution info's -func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { - params := k.GetParams(ctx) - taxRate := k.GetTaxRate(ctx, sdk.ZeroInt()) - rewardWeight := k.GetRewardWeight(ctx, util.GetEpoch(ctx)) - - return NewGenesisState(params, taxRate, rewardWeight) + // store tax cap for SDT & LUNA(no tax) + keeper.SetTaxCap(ctx, data.Params.TaxPolicy.Cap.Denom, data.Params.TaxPolicy.Cap.Amount) + keeper.SetTaxCap(ctx, core.MicroLunaDenom, sdk.ZeroInt()) } -// ValidateGenesis validates the provided treasury genesis state to ensure the -// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) -func ValidateGenesis(data GenesisState) error { - if data.GenesisTaxRate.GT(data.Params.TaxPolicy.RateMax) || - data.GenesisTaxRate.LT(data.Params.TaxPolicy.RateMin) { - return fmt.Errorf("Genesis tax rate must be between %s and %s, is %s", - data.Params.TaxPolicy.RateMin, data.Params.TaxPolicy.RateMax, data.GenesisTaxRate) - } - - if data.GenesisRewardWeight.GT(data.Params.RewardPolicy.RateMax) || - data.GenesisRewardWeight.LT(data.Params.RewardPolicy.RateMin) { - return fmt.Errorf("Genesis reward rate must be between %s and %s, is %s", - data.Params.RewardPolicy.RateMin, data.Params.RewardPolicy.RateMax, data.GenesisRewardWeight) - } - - return validateParams(data.Params) +// ExportGenesis writes the current store values +// to a genesis file, which can be imported again +// with InitGenesis +func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { + params := keeper.GetParams(ctx) + taxRate := keeper.GetTaxRate(ctx, core.GetEpoch(ctx)) + rewardWeight := keeper.GetRewardWeight(ctx, core.GetEpoch(ctx)) + return NewGenesisState(params, taxRate, rewardWeight) } diff --git a/x/treasury/indicator_test.go b/x/treasury/indicator_test.go deleted file mode 100644 index 87f020f16..000000000 --- a/x/treasury/indicator_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package treasury - -import ( - "testing" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestFeeRewardsForEpoch(t *testing.T) { - input := createTestInput(t) - - taxAmount := sdk.NewInt(1000).MulRaw(assets.MicroUnit) - - // Set random prices - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(1)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, sdk.NewDec(10)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, sdk.NewDec(100)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, sdk.NewDec(1000)) - - // Record tax proceeds - input.treasuryKeeper.RecordTaxProceeds(input.ctx, sdk.Coins{ - sdk.NewCoin(assets.MicroSDRDenom, taxAmount), - sdk.NewCoin(assets.MicroKRWDenom, taxAmount), - sdk.NewCoin(assets.MicroGBPDenom, taxAmount), - sdk.NewCoin(assets.MicroCNYDenom, taxAmount), - }) - - // Get taxes - taxProceedsInSDR := TaxRewardsForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - require.Equal(t, sdk.NewDec(1111).MulInt64(assets.MicroUnit), taxProceedsInSDR) -} - -func TestSeigniorageRewardsForEpoch(t *testing.T) { - input := createTestInput(t) - - sAmt := sdk.NewInt(1000) - lnasdrRate := sdk.NewDec(10) - - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sAmt)) - - SeigniorageRewardsForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - - // Set random prices - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, lnasdrRate) - - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch) - - // Add seigniorage - input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sAmt)) - - // Get seigniorage rewards - seigniorageProceeds := SeigniorageRewardsForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - miningRewardWeight := input.treasuryKeeper.GetRewardWeight(input.ctx, util.GetEpoch(input.ctx)) - require.Equal(t, lnasdrRate.MulInt(sAmt).Mul(miningRewardWeight), seigniorageProceeds) -} - -func TestMiningRewardsForEpoch(t *testing.T) { - input := createTestInput(t) - - amt := sdk.NewInt(1000).MulRaw(assets.MicroUnit) - - // Set random prices - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, sdk.NewDec(1)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(10)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroGBPDenom, sdk.NewDec(100)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, sdk.NewDec(1000)) - - // Record tax proceeds - input.treasuryKeeper.RecordTaxProceeds(input.ctx, sdk.Coins{ - sdk.NewCoin(assets.MicroSDRDenom, amt), - sdk.NewCoin(assets.MicroKRWDenom, amt), - sdk.NewCoin(assets.MicroGBPDenom, amt), - sdk.NewCoin(assets.MicroCNYDenom, amt), - }) - - // Add seigniorage - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, amt)) - - tProceeds := TaxRewardsForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - sProceeds := SeigniorageRewardsForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - mProceeds := MiningRewardForEpoch(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx)) - - require.Equal(t, tProceeds.Add(sProceeds), mProceeds) -} - -func TestUnitIndicator(t *testing.T) { - input := createTestInput(t) - - lunaTotalBondedAmount := input.treasuryKeeper.valset.TotalBondedTokens(input.ctx) - - // Just get an indicator to multiply the unit value by the expected rval. - // the unit indicator function obviously should return the expected rval. - actual := UnitLunaIndicator(input.ctx, input.treasuryKeeper, util.GetEpoch(input.ctx), - func(_ sdk.Context, _ Keeper, _ sdk.Int) sdk.Dec { - return sdk.NewDecFromInt(lunaTotalBondedAmount.MulRaw(20)) - }) - - require.Equal(t, sdk.NewDec(20), actual) -} - -func linearFn(_ sdk.Context, _ Keeper, epoch sdk.Int) sdk.Dec { - return sdk.NewDecFromInt(epoch) -} - -func TestSumIndicator(t *testing.T) { - input := createTestInput(t) - - // Case 1: at epoch 0 and summing over 0 epochs - rval := SumIndicator(input.ctx, input.treasuryKeeper, sdk.ZeroInt(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 2: at epoch 0 and summing over negative epochs - rval = SumIndicator(input.ctx, input.treasuryKeeper, sdk.OneInt().Neg(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 3: at epoch 3 and summing over 3, 4, 5 epochs; all should have the same rval - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch * 3) - rval = SumIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(4), linearFn) - rval2 := SumIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(5), linearFn) - rval3 := SumIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(6), linearFn) - require.Equal(t, sdk.NewDec(6), rval) - require.Equal(t, rval, rval2) - require.Equal(t, rval2, rval3) - - // Case 4: at epoch 3 and summing over 0 epochs - rval = SumIndicator(input.ctx, input.treasuryKeeper, sdk.ZeroInt(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 5. Sum up to 10 - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch * 10) - rval = SumIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(10), linearFn) - require.Equal(t, sdk.NewDec(55), rval) -} - -func TestRollingAverageIndicator(t *testing.T) { - input := createTestInput(t) - - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(100000000*assets.MicroUnit))) - - // Case 1: at epoch 0 and averaging over 0 epochs - rval := RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.ZeroInt(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 2: at epoch 0 and averaging over negative epochs - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.OneInt().Neg(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 3: at epoch 3 and averaging over 3, 4, 5 epochs; all should have the same rval - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch * 3) - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(4), linearFn) - rval2 := RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(5), linearFn) - rval3 := RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(6), linearFn) - require.Equal(t, sdk.NewDecWithPrec(15, 1), rval) - require.Equal(t, rval, rval2) - require.Equal(t, rval2, rval3) - - // Case 4: at epoch 3 and averaging over 0 epochs - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.ZeroInt(), linearFn) - require.Equal(t, sdk.ZeroDec(), rval) - - // Case 5: at epoch 3 and averaging over 1 epoch - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.OneInt(), linearFn) - require.Equal(t, sdk.NewDec(3), rval) - - // Case 6: at epoch 500 and averaging over 300 epochs - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch * 500) - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), linearFn) - require.Equal(t, sdk.NewDecWithPrec(3505, 1), rval) - - // Test all of our reporting functions - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.OneDec()) - input.mintKeeper.PeekEpochSeigniorage(input.ctx, sdk.ZeroInt()) - - for i := int64(201); i <= 500; i++ { - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch * i) - input.treasuryKeeper.RecordTaxProceeds(input.ctx, sdk.Coins{sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(i).MulRaw(assets.MicroUnit))}) - input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(i).MulRaw(assets.MicroUnit))) - - input.treasuryKeeper.SetRewardWeight(input.ctx, sdk.OneDec()) - } - - totalBondedTokens := sdk.NewDecFromInt(input.treasuryKeeper.valset.TotalBondedTokens(input.ctx)) - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), TaxRewardsForEpoch) - require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(assets.MicroUnit), rval) - - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), SeigniorageRewardsForEpoch) - require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(assets.MicroUnit), rval) - - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), MiningRewardForEpoch) - require.Equal(t, sdk.NewDecWithPrec(3505*2, 1).MulInt64(assets.MicroUnit), rval) - - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), TRL) - require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(assets.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.Mul(sdk.NewDec(1000000)).TruncateInt()) - - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), SRL) - require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(assets.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.MulTruncate(sdk.NewDec(1000000)).TruncateInt()) - - rval = RollingAverageIndicator(input.ctx, input.treasuryKeeper, sdk.NewInt(300), MRL) - require.Equal(t, sdk.NewDecWithPrec(3505*2, 1).MulInt64(assets.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.MulTruncate(sdk.NewDec(1000000)).TruncateInt()) -} diff --git a/x/treasury/indicator.go b/x/treasury/internal/keeper/indicator.go similarity index 60% rename from x/treasury/indicator.go rename to x/treasury/internal/keeper/indicator.go index 090ee2e70..7b66931c8 100644 --- a/x/treasury/indicator.go +++ b/x/treasury/internal/keeper/indicator.go @@ -1,8 +1,7 @@ -package treasury +package keeper import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" + core "github.com/terra-project/core/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -19,13 +18,13 @@ import ( // // TaxRewardsForEpoch returns tax rewards that have been collected in the epoch -func TaxRewardsForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { +func TaxRewardsForEpoch(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { taxRewards := sdk.NewDecCoins(k.PeekTaxProceeds(ctx, epoch)) taxRewardInMicroSDR := sdk.ZeroDec() for _, coinReward := range taxRewards { - if coinReward.Denom != assets.MicroSDRDenom { - swappedReward, err := k.mk.GetSwapDecCoin(ctx, coinReward, assets.MicroSDRDenom) + if coinReward.Denom != core.MicroSDRDenom { + swappedReward, err := k.marketKeeper.GetSwapDecCoin(ctx, coinReward, core.MicroSDRDenom) if err != nil { continue } @@ -39,12 +38,12 @@ func TaxRewardsForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { } // SeigniorageRewardsForEpoch returns seigniorage rewards for the epoch -func SeigniorageRewardsForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { - seignioragePool := k.mtk.PeekEpochSeigniorage(ctx, epoch) +func SeigniorageRewardsForEpoch(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { + seignioragePool := k.PeekEpochSeigniorage(ctx, epoch) rewardAmt := k.GetRewardWeight(ctx, epoch).MulInt(seignioragePool) - seigniorageReward := sdk.NewDecCoinFromDec(assets.MicroLunaDenom, rewardAmt) + seigniorageReward := sdk.NewDecCoinFromDec(core.MicroLunaDenom, rewardAmt) - microSDRReward, err := k.mk.GetSwapDecCoin(ctx, seigniorageReward, assets.MicroSDRDenom) + microSDRReward, err := k.marketKeeper.GetSwapDecCoin(ctx, seigniorageReward, core.MicroSDRDenom) if err != nil { return sdk.ZeroDec() } @@ -53,7 +52,7 @@ func SeigniorageRewardsForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.De } // MiningRewardForEpoch returns the sum of tax and seigniorage rewards for the epoch -func MiningRewardForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { +func MiningRewardForEpoch(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { taxRewards := TaxRewardsForEpoch(ctx, k, epoch) seigniorageRewards := SeigniorageRewardsForEpoch(ctx, k, epoch) @@ -61,37 +60,37 @@ func MiningRewardForEpoch(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { } // TRL returns tax rewards / luna / epoch -func TRL(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { +func TRL(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { return UnitLunaIndicator(ctx, k, epoch, TaxRewardsForEpoch) } // SRL returns Seigniorage rewards / luna / epoch -func SRL(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { +func SRL(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { return UnitLunaIndicator(ctx, k, epoch, SeigniorageRewardsForEpoch) } // MRL returns mining rewards / luna / epoch -func MRL(ctx sdk.Context, k Keeper, epoch sdk.Int) sdk.Dec { +func MRL(ctx sdk.Context, k Keeper, epoch int64) sdk.Dec { return UnitLunaIndicator(ctx, k, epoch, MiningRewardForEpoch) } // UnitLunaIndicator evaluates the indicator function and divides it by the luna supply for the epoch -func UnitLunaIndicator(ctx sdk.Context, k Keeper, epoch sdk.Int, - indicatorFunction func(sdk.Context, Keeper, sdk.Int) sdk.Dec) sdk.Dec { +func UnitLunaIndicator(ctx sdk.Context, k Keeper, epoch int64, + indicatorFunction func(sdk.Context, Keeper, int64) sdk.Dec) sdk.Dec { indicator := indicatorFunction(ctx, k, epoch) - lunaTotalBondedAmount := k.valset.TotalBondedTokens(ctx) + lunaTotalBondedAmount := k.stakingKeeper.TotalBondedTokens(ctx) return indicator.QuoInt(lunaTotalBondedAmount) } // SumIndicator returns the sum of the indicator over several epochs. // If current epoch < epochs, we return the best we can and return SumIndicator(currentEpoch) -func SumIndicator(ctx sdk.Context, k Keeper, epochs sdk.Int, - indicatorFunction func(sdk.Context, Keeper, sdk.Int) sdk.Dec) sdk.Dec { +func SumIndicator(ctx sdk.Context, k Keeper, epochs int64, + indicatorFunction func(sdk.Context, Keeper, int64) sdk.Dec) sdk.Dec { sum := sdk.ZeroDec() - var i sdk.Int - curEpoch := util.GetEpoch(ctx) - for i = curEpoch; i.GTE(sdk.ZeroInt()) && i.GT(curEpoch.Sub(epochs)); i = i.Sub(sdk.OneInt()) { + curEpoch := core.GetEpoch(ctx) + + for i := curEpoch; i >= 0 && i > (curEpoch-epochs); i-- { val := indicatorFunction(ctx, k, i) sum = sum.Add(val) } @@ -101,20 +100,21 @@ func SumIndicator(ctx sdk.Context, k Keeper, epochs sdk.Int, // RollingAverageIndicator returns the rolling average of the indicator over several epochs. // If current epoch < epochs, we return the best we can and return RollingAverageIndicator(currentEpoch) -func RollingAverageIndicator(ctx sdk.Context, k Keeper, epochs sdk.Int, - indicatorFunction func(sdk.Context, Keeper, sdk.Int) sdk.Dec) sdk.Dec { +func RollingAverageIndicator(ctx sdk.Context, k Keeper, epochs int64, + indicatorFunction func(sdk.Context, Keeper, int64) sdk.Dec) sdk.Dec { sum := sdk.ZeroDec() - var i sdk.Int - curEpoch := util.GetEpoch(ctx) - for i = curEpoch; i.GTE(sdk.ZeroInt()) && i.GT(curEpoch.Sub(epochs)); i = i.Sub(sdk.OneInt()) { + curEpoch := core.GetEpoch(ctx) + + var i int64 + for i = curEpoch; i >= 0 && i > (curEpoch-epochs); i-- { val := indicatorFunction(ctx, k, i) sum = sum.Add(val) } - computedEpochs := curEpoch.Sub(i) - if computedEpochs.Equal(sdk.ZeroInt()) { + computedEpochs := curEpoch - i + if computedEpochs == 0 { return sum } - return sum.QuoInt(computedEpochs) + return sum.QuoInt64(computedEpochs) } diff --git a/x/treasury/internal/keeper/indicator_test.go b/x/treasury/internal/keeper/indicator_test.go new file mode 100644 index 000000000..8c8663d02 --- /dev/null +++ b/x/treasury/internal/keeper/indicator_test.go @@ -0,0 +1,244 @@ +package keeper + +import ( + "fmt" + "testing" + + core "github.com/terra-project/core/types" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +func TestFeeRewardsForEpoch(t *testing.T) { + input := CreateTestInput(t) + + taxAmount := sdk.NewInt(1000).MulRaw(core.MicroUnit) + + // Set random prices + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdk.NewDec(1)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, sdk.NewDec(10)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroGBPDenom, sdk.NewDec(100)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroCNYDenom, sdk.NewDec(1000)) + + // Record tax proceeds + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, sdk.Coins{ + sdk.NewCoin(core.MicroSDRDenom, taxAmount), + sdk.NewCoin(core.MicroKRWDenom, taxAmount), + sdk.NewCoin(core.MicroGBPDenom, taxAmount), + sdk.NewCoin(core.MicroCNYDenom, taxAmount), + }) + + // Get taxes + taxProceedsInSDR := TaxRewardsForEpoch(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx)) + require.Equal(t, sdk.NewDec(1111).MulInt64(core.MicroUnit), taxProceedsInSDR) +} + +func TestSeigniorageRewardsForEpoch(t *testing.T) { + input := CreateTestInput(t) + + sAmt := sdk.NewInt(1000) + lnasdrRate := sdk.NewDec(10) + + // Add seigniorage + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sAmt))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + // Set random prices + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, lnasdrRate) + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch) + + // Add seigniorage + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.ZeroInt()))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + // Get seigniorage rewards + seigniorageProceeds := SeigniorageRewardsForEpoch(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx)) + miningRewardWeight := input.TreasuryKeeper.GetRewardWeight(input.Ctx, core.GetEpoch(input.Ctx)) + require.Equal(t, lnasdrRate.MulInt(sAmt).Mul(miningRewardWeight), seigniorageProceeds) +} + +func TestMiningRewardsForEpoch(t *testing.T) { + input := CreateTestInput(t) + + amt := sdk.NewInt(1000).MulRaw(core.MicroUnit) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, amt))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + // Set random prices + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, sdk.NewDec(1)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdk.NewDec(10)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroGBPDenom, sdk.NewDec(100)) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroCNYDenom, sdk.NewDec(1000)) + + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch) + + // Record tax proceeds + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, sdk.Coins{ + sdk.NewCoin(core.MicroSDRDenom, amt), + sdk.NewCoin(core.MicroKRWDenom, amt), + sdk.NewCoin(core.MicroGBPDenom, amt), + sdk.NewCoin(core.MicroCNYDenom, amt), + }) + + // Add seigniorage + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.ZeroInt()))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + tProceeds := TaxRewardsForEpoch(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx)) + sProceeds := SeigniorageRewardsForEpoch(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx)) + mProceeds := MiningRewardForEpoch(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx)) + + require.Equal(t, tProceeds.Add(sProceeds), mProceeds) +} + +func TestUnitIndicator(t *testing.T) { + input := CreateTestInput(t) + sh := staking.NewHandler(input.StakingKeeper) + + // Create Validators + amt := sdk.TokensFromConsensusPower(100) + addr, val := ValAddrs[0], PubKeys[0] + addr1, val1 := ValAddrs[1], PubKeys[1] + res := sh(input.Ctx, NewTestMsgCreateValidator(addr, val, amt)) + fmt.Println(res) + require.True(t, res.IsOK()) + res = sh(input.Ctx, NewTestMsgCreateValidator(addr1, val1, amt)) + require.True(t, res.IsOK()) + staking.EndBlocker(input.Ctx, input.StakingKeeper) + + lunaTotalBondedAmount := input.StakingKeeper.TotalBondedTokens(input.Ctx) + + // Just get an indicator to multiply the unit value by the expected rval. + // the unit indicator function obviously should return the expected rval. + actual := UnitLunaIndicator(input.Ctx, input.TreasuryKeeper, core.GetEpoch(input.Ctx), + func(_ sdk.Context, _ Keeper, _ int64) sdk.Dec { + return sdk.NewDecFromInt(lunaTotalBondedAmount.MulRaw(20)) + }) + + require.Equal(t, sdk.NewDec(20), actual) +} + +func linearFn(_ sdk.Context, _ Keeper, epoch int64) sdk.Dec { + return sdk.NewDec(epoch) +} + +func TestSumIndicator(t *testing.T) { + input := CreateTestInput(t) + + // Case 1: at epoch 0 and summing over 0 epochs + rval := SumIndicator(input.Ctx, input.TreasuryKeeper, 0, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 2: at epoch 0 and summing over negative epochs + rval = SumIndicator(input.Ctx, input.TreasuryKeeper, -1, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 3: at epoch 3 and summing over 3, 4, 5 epochs; all should have the same rval + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * 3) + rval = SumIndicator(input.Ctx, input.TreasuryKeeper, 4, linearFn) + rval2 := SumIndicator(input.Ctx, input.TreasuryKeeper, 5, linearFn) + rval3 := SumIndicator(input.Ctx, input.TreasuryKeeper, 6, linearFn) + require.Equal(t, sdk.NewDec(6), rval) + require.Equal(t, rval, rval2) + require.Equal(t, rval2, rval3) + + // Case 4: at epoch 3 and summing over 0 epochs + rval = SumIndicator(input.Ctx, input.TreasuryKeeper, 0, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 5. Sum up to 10 + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * 10) + rval = SumIndicator(input.Ctx, input.TreasuryKeeper, 10, linearFn) + require.Equal(t, sdk.NewDec(55), rval) +} + +func TestRollingAverageIndicator(t *testing.T) { + input := CreateTestInput(t) + sh := staking.NewHandler(input.StakingKeeper) + + // Create Validators + amt := sdk.TokensFromConsensusPower(1) + addr, val := ValAddrs[0], PubKeys[0] + addr1, val1 := ValAddrs[1], PubKeys[1] + res := sh(input.Ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, res.IsOK()) + res = sh(input.Ctx, NewTestMsgCreateValidator(addr1, val1, amt)) + require.True(t, res.IsOK()) + staking.EndBlocker(input.Ctx, input.StakingKeeper) + + // Case 1: at epoch 0 and averaging over 0 epochs + rval := RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 0, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 2: at epoch 0 and averaging over negative epochs + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, -1, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 3: at epoch 3 and averaging over 3, 4, 5 epochs; all should have the same rval + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * 3) + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 4, linearFn) + rval2 := RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 5, linearFn) + rval3 := RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 6, linearFn) + require.Equal(t, sdk.NewDecWithPrec(15, 1), rval) + require.Equal(t, rval, rval2) + require.Equal(t, rval2, rval3) + + // Case 4: at epoch 3 and averaging over 0 epochs + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 0, linearFn) + require.Equal(t, sdk.ZeroDec(), rval) + + // Case 5: at epoch 3 and averaging over 1 epoch + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 1, linearFn) + require.Equal(t, sdk.NewDec(3), rval) + + // Case 6: at epoch 500 and averaging over 300 epochs + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * 500) + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, linearFn) + require.Equal(t, sdk.NewDecWithPrec(3505, 1), rval) + + // Test all of our reporting functions + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdk.OneDec()) + + // set initial supply + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * 200) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(100000000*core.MicroUnit)))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + for i := int64(201); i <= 500; i++ { + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch * i) + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, sdk.Coins{sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(i).MulRaw(core.MicroUnit))}) + input.TreasuryKeeper.SetRewardWeight(input.Ctx, sdk.OneDec()) + + supply = supply.SetTotal(supply.GetTotal().Sub(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(i).MulRaw(core.MicroUnit))))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + } + + totalBondedTokens := sdk.NewDecFromInt(input.StakingKeeper.TotalBondedTokens(input.Ctx)) + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, TaxRewardsForEpoch) + require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(core.MicroUnit), rval) + + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, SeigniorageRewardsForEpoch) + require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(core.MicroUnit), rval) + + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, MiningRewardForEpoch) + require.Equal(t, sdk.NewDecWithPrec(3505*2, 1).MulInt64(core.MicroUnit), rval) + + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, TRL) + require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(core.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.Mul(sdk.NewDec(1000000)).TruncateInt()) + + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, SRL) + require.Equal(t, sdk.NewDecWithPrec(3505, 1).MulInt64(core.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.MulTruncate(sdk.NewDec(1000000)).TruncateInt()) + + rval = RollingAverageIndicator(input.Ctx, input.TreasuryKeeper, 300, MRL) + require.Equal(t, sdk.NewDecWithPrec(3505*2, 1).MulInt64(core.MicroUnit).Quo(totalBondedTokens).Mul(sdk.NewDec(1000000)).TruncateInt(), rval.MulTruncate(sdk.NewDec(1000000)).TruncateInt()) +} diff --git a/x/treasury/internal/keeper/keeper.go b/x/treasury/internal/keeper/keeper.go new file mode 100644 index 000000000..66cb8690b --- /dev/null +++ b/x/treasury/internal/keeper/keeper.go @@ -0,0 +1,194 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + + core "github.com/terra-project/core/types" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/terra-project/core/x/treasury/internal/types" +) + +// Keeper of the treasury store +type Keeper struct { + cdc *codec.Codec + storeKey sdk.StoreKey + + paramSpace params.Subspace + codespace sdk.CodespaceType + + supplyKeeper types.SupplyKeeper + marketKeeper types.MarketKeeper + stakingKeeper types.StakingKeeper + distrKeeper types.DistributionKeeper + + oracleModuleName string + distributionModuleName string +} + +// NewKeeper creates a new treasury Keeper instance +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, + supplyKeeper types.SupplyKeeper, marketKeeper types.MarketKeeper, + stakingKeeper types.StakingKeeper, distrKeeper types.DistributionKeeper, + oracleModuleName string, distributionModuleName string, codespace sdk.CodespaceType) Keeper { + + return Keeper{ + cdc: cdc, + storeKey: storeKey, + paramSpace: paramSpace.WithKeyTable(ParamKeyTable()), + codespace: codespace, + supplyKeeper: supplyKeeper, + marketKeeper: marketKeeper, + stakingKeeper: stakingKeeper, + distrKeeper: distrKeeper, + oracleModuleName: oracleModuleName, + distributionModuleName: distributionModuleName, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// return the codespace +func (k Keeper) Codespace() sdk.CodespaceType { + return k.codespace +} + +// Load the tax-rate +func (k Keeper) GetTaxRate(ctx sdk.Context, epoch int64) (taxRate sdk.Dec) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetTaxRateKey(epoch)) + if b == nil { + return types.DefaultTaxRate + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &taxRate) + return +} + +// Set the tax-rate +func (k Keeper) SetTaxRate(ctx sdk.Context, taxRate sdk.Dec) { + epoch := core.GetEpoch(ctx) + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinaryLengthPrefixed(taxRate) + store.Set(types.GetTaxRateKey(epoch), b) +} + +// Load the reward weight +func (k Keeper) GetRewardWeight(ctx sdk.Context, epoch int64) (rewardWeight sdk.Dec) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.GetRewardWeightKey(epoch)) + if b == nil { + return types.DefaultRewardWeight + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &rewardWeight) + return +} + +// Set the reward weight +func (k Keeper) SetRewardWeight(ctx sdk.Context, rewardWeight sdk.Dec) { + epoch := core.GetEpoch(ctx) + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinaryLengthPrefixed(rewardWeight) + store.Set(types.GetRewardWeightKey(epoch), b) +} + +// setTaxCap sets the Tax Cap. Denominated in integer units of the reference {denom} +func (k Keeper) SetTaxCap(ctx sdk.Context, denom string, cap sdk.Int) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(cap) + store.Set(types.GetTaxCapKey(denom), bz) +} + +// GetTaxCap gets the Tax Cap. Denominated in integer units of the reference {denom} +func (k Keeper) GetTaxCap(ctx sdk.Context, denom string) (taxCap sdk.Int) { + store := ctx.KVStore(k.storeKey) + + bz := store.Get(types.GetTaxCapKey(denom)) + if bz == nil { + // if no tax-cap registered, return SDR tax-cap + return k.TaxPolicy(ctx).Cap.Amount + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &taxCap) + return +} + +// RecordTaxProceeds add tax proceeds that have been added this epoch +func (k Keeper) RecordTaxProceeds(ctx sdk.Context, delta sdk.Coins) { + if delta.Empty() { + return + } + + epoch := core.GetEpoch(ctx) + proceeds := k.PeekTaxProceeds(ctx, epoch) + proceeds = proceeds.Add(delta) + + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(proceeds) + store.Set(types.GetTaxProceedsKey(epoch), bz) +} + +// PeekTaxProceeds peeks the total amount of taxes that have been collected in the given epoch. +func (k Keeper) PeekTaxProceeds(ctx sdk.Context, epoch int64) (res sdk.Coins) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetTaxProceedsKey(epoch)) + if bz == nil { + res = sdk.Coins{} + } else { + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) + } + return +} + +// UpdateIssuance update epoch issuance from supply keeper (historical) +func (k Keeper) UpdateIssuance(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + + epoch := core.GetEpoch(ctx) + totalCoins := k.supplyKeeper.GetSupply(ctx).GetTotal() + bz := k.cdc.MustMarshalBinaryLengthPrefixed(totalCoins) + store.Set(types.GetHistoricalIssuanceKey(epoch), bz) +} + +// GetHistoricalIssuance returns epoch issuance of a denom +func (k Keeper) GetHistoricalIssuance(ctx sdk.Context, epoch int64) (res sdk.Coins) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetHistoricalIssuanceKey(epoch)) + + if bz == nil { + res = sdk.Coins{} + } else { + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) + } + return +} + +// PeekEpochSeigniorage retursn epoch seigniorage +func (k Keeper) PeekEpochSeigniorage(ctx sdk.Context, epoch int64) sdk.Int { + if epoch == 0 { + return sdk.ZeroInt() + } + + epochIssuance := k.GetHistoricalIssuance(ctx, epoch).AmountOf(core.MicroLunaDenom) + if epochIssuance.IsZero() { + epochIssuance = k.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf(core.MicroLunaDenom) + } + + preEpochIssuance := k.GetHistoricalIssuance(ctx, epoch-1).AmountOf(core.MicroLunaDenom) + epochSeigniorage := preEpochIssuance.Sub(epochIssuance) + + if epochSeigniorage.LT(sdk.ZeroInt()) { + return sdk.ZeroInt() + } + + return epochSeigniorage +} diff --git a/x/treasury/internal/keeper/keeper_test.go b/x/treasury/internal/keeper/keeper_test.go new file mode 100644 index 000000000..be8b55bba --- /dev/null +++ b/x/treasury/internal/keeper/keeper_test.go @@ -0,0 +1,137 @@ +package keeper + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/treasury/internal/types" +) + +func TestRewardWeight(t *testing.T) { + input := CreateTestInput(t) + + // See that we can get and set reward weights + blocksPerEpoch := core.BlocksPerEpoch + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + input.TreasuryKeeper.SetRewardWeight(input.Ctx, sdk.NewDecWithPrec(i, 2)) + } + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + require.Equal(t, sdk.NewDecWithPrec(i, 2), input.TreasuryKeeper.GetRewardWeight(input.Ctx, i)) + } +} + +func TestTaxRate(t *testing.T) { + input := CreateTestInput(t) + + // See that we can get and set tax rate + blocksPerEpoch := core.BlocksPerEpoch + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + input.TreasuryKeeper.SetTaxRate(input.Ctx, sdk.NewDecWithPrec(i, 2)) + } + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + require.Equal(t, sdk.NewDecWithPrec(i, 2), input.TreasuryKeeper.GetTaxRate(input.Ctx, i)) + } +} + +func TestTaxCap(t *testing.T) { + input := CreateTestInput(t) + + for i := int64(0); i < 10; i++ { + input.TreasuryKeeper.SetTaxCap(input.Ctx, core.MicroCNYDenom, sdk.NewInt(i)) + require.Equal(t, sdk.NewInt(i), input.TreasuryKeeper.GetTaxCap(input.Ctx, core.MicroCNYDenom)) + } +} + +func TestTaxProceeds(t *testing.T) { + input := CreateTestInput(t) + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * core.BlocksPerEpoch) + + proceeds := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(i))) + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, proceeds) + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, proceeds) + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, proceeds) + } + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * core.BlocksPerEpoch) + proceeds := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(i*3))) + + require.Equal(t, proceeds, input.TreasuryKeeper.PeekTaxProceeds(input.Ctx, i)) + } +} + +func TestMicroLunaIssuance(t *testing.T) { + input := CreateTestInput(t) + + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.ZeroInt()))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + // See that we can get and set luna issuance + blocksPerEpoch := core.BlocksPerEpoch + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(i)))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + } + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * blocksPerEpoch) + + require.Equal(t, sdk.NewInt(i), input.TreasuryKeeper.GetHistoricalIssuance(input.Ctx, i).AmountOf(core.MicroLunaDenom)) + } +} + +func TestPeekEpochSeigniorage(t *testing.T) { + input := CreateTestInput(t) + + for i := int64(0); i < 10; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * core.BlocksPerEpoch) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + + preIssuance := sdk.NewInt(rand.Int63() + 1) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, preIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + nowIssuance := sdk.NewInt(rand.Int63() + 1) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, nowIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + targetSeigniorage := preIssuance.Sub(nowIssuance) + if targetSeigniorage.IsNegative() { + targetSeigniorage = sdk.ZeroInt() + } + + require.Equal(t, targetSeigniorage, input.TreasuryKeeper.PeekEpochSeigniorage(input.Ctx, i+1)) + } +} + +func TestParams(t *testing.T) { + input := CreateTestInput(t) + + defaultParams := types.DefaultParams() + input.TreasuryKeeper.SetParams(input.Ctx, defaultParams) + + retrievedParams := input.TreasuryKeeper.GetParams(input.Ctx) + require.Equal(t, defaultParams, retrievedParams) +} diff --git a/x/treasury/internal/keeper/params.go b/x/treasury/internal/keeper/params.go new file mode 100644 index 000000000..0e6862623 --- /dev/null +++ b/x/treasury/internal/keeper/params.go @@ -0,0 +1,65 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/terra-project/core/x/treasury/internal/types" +) + +// ParamTable for treasury module +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&types.Params{}) +} + +// TaxPolicy +func (k Keeper) TaxPolicy(ctx sdk.Context) (res types.PolicyConstraints) { + k.paramSpace.Get(ctx, types.ParamStoreKeyTaxPolicy, &res) + return +} + +// RewardPolicy +func (k Keeper) RewardPolicy(ctx sdk.Context) (res types.PolicyConstraints) { + k.paramSpace.Get(ctx, types.ParamStoreKeyRewardPolicy, &res) + return +} + +// SeigniorageBurdenTarget +func (k Keeper) SeigniorageBurdenTarget(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeySeigniorageBurdenTarget, &res) + return +} + +// MiningIncrement +func (k Keeper) MiningIncrement(ctx sdk.Context) (res sdk.Dec) { + k.paramSpace.Get(ctx, types.ParamStoreKeyMiningIncrement, &res) + return +} + +// WindowShort +func (k Keeper) WindowShort(ctx sdk.Context) (res int64) { + k.paramSpace.Get(ctx, types.ParamStoreKeyWindowShort, &res) + return +} + +// WindowLong +func (k Keeper) WindowLong(ctx sdk.Context) (res int64) { + k.paramSpace.Get(ctx, types.ParamStoreKeyWindowLong, &res) + return +} + +// WindowProbation +func (k Keeper) WindowProbation(ctx sdk.Context) (res int64) { + k.paramSpace.Get(ctx, types.ParamStoreKeyWindowProbation, &res) + return +} + +// GetParams returns the total set of market parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of market parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/treasury/policy.go b/x/treasury/internal/keeper/policy.go similarity index 62% rename from x/treasury/policy.go rename to x/treasury/internal/keeper/policy.go index 229a6924c..eabd9476e 100644 --- a/x/treasury/policy.go +++ b/x/treasury/internal/keeper/policy.go @@ -1,16 +1,38 @@ -package treasury +package keeper import ( - "github.com/terra-project/core/types/util" + core "github.com/terra-project/core/types" sdk "github.com/cosmos/cosmos-sdk/types" ) +// UpdateTaxCap updates all denom's tax cap +func (k Keeper) UpdateTaxCap(ctx sdk.Context) sdk.Coins { + cap := k.GetParams(ctx).TaxPolicy.Cap + total := k.supplyKeeper.GetSupply(ctx).GetTotal() + + var newCaps sdk.Coins + for _, coin := range total { + // ignore uluna tax cap (uluna has no tax); keep sdr tax cap + if coin.Denom == core.MicroLunaDenom || coin.Denom == core.MicroSDRDenom { + continue + } + + newCap, _, err := k.marketKeeper.GetSwapCoin(ctx, cap, coin.Denom, true) + if err == nil { + newCaps = append(newCaps, newCap) + k.SetTaxCap(ctx, newCap.Denom, newCap.Amount) + } + } + + return newCaps +} + // t(t+1) = t(t) * (TL_year(t) + INC) / TL_month(t) -func updateTaxPolicy(ctx sdk.Context, k Keeper) (newTaxRate sdk.Dec) { +func (k Keeper) UpdateTaxPolicy(ctx sdk.Context) (newTaxRate sdk.Dec) { params := k.GetParams(ctx) - oldTaxRate := k.GetTaxRate(ctx, util.GetEpoch(ctx)) + oldTaxRate := k.GetTaxRate(ctx, core.GetEpoch(ctx)) inc := params.MiningIncrement tlYear := RollingAverageIndicator(ctx, k, params.WindowLong, TRL) tlMonth := RollingAverageIndicator(ctx, k, params.WindowShort, TRL) @@ -30,10 +52,10 @@ func updateTaxPolicy(ctx sdk.Context, k Keeper) (newTaxRate sdk.Dec) { } // w(t+1) = w(t)*SB_target/SB_rolling(t) -func updateRewardPolicy(ctx sdk.Context, k Keeper) (newRewardWeight sdk.Dec) { +func (k Keeper) UpdateRewardPolicy(ctx sdk.Context) (newRewardWeight sdk.Dec) { params := k.GetParams(ctx) - curEpoch := util.GetEpoch(ctx) + curEpoch := core.GetEpoch(ctx) oldWeight := k.GetRewardWeight(ctx, curEpoch) sbTarget := params.SeigniorageBurdenTarget diff --git a/x/treasury/internal/keeper/policy_test.go b/x/treasury/internal/keeper/policy_test.go new file mode 100644 index 000000000..14bfd1391 --- /dev/null +++ b/x/treasury/internal/keeper/policy_test.go @@ -0,0 +1,87 @@ +package keeper + +import ( + "testing" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/treasury/internal/types" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +func TestUpdateTaxRate(t *testing.T) { + input := CreateTestInput(t) + sh := staking.NewHandler(input.StakingKeeper) + + // Create Validators + amt := sdk.TokensFromConsensusPower(1) + addr, val := ValAddrs[0], PubKeys[0] + addr1, val1 := ValAddrs[1], PubKeys[1] + res := sh(input.Ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, res.IsOK()) + res = sh(input.Ctx, NewTestMsgCreateValidator(addr1, val1, amt)) + require.True(t, res.IsOK()) + staking.EndBlocker(input.Ctx, input.StakingKeeper) + + windowLong := input.TreasuryKeeper.WindowLong(input.Ctx) + taxPolicy := input.TreasuryKeeper.TaxPolicy(input.Ctx) + + // zero reward tax proceeds + for i := int64(0); i < windowLong; i++ { + input.Ctx = input.Ctx.WithBlockHeight(i * core.BlocksPerEpoch) + + taxProceeds := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.ZeroInt())) + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, taxProceeds) + } + + input.TreasuryKeeper.UpdateTaxPolicy(input.Ctx) + taxRate := input.TreasuryKeeper.GetTaxRate(input.Ctx, core.GetEpoch(input.Ctx)) + require.Equal(t, types.DefaultTaxRate.Add(taxPolicy.ChangeRateMax), taxRate) +} + +func TestUpdateRewardWeight(t *testing.T) { + input := CreateTestInput(t) + sh := staking.NewHandler(input.StakingKeeper) + + // Create Validators + amt := sdk.TokensFromConsensusPower(1) + addr, val := ValAddrs[0], PubKeys[0] + addr1, val1 := ValAddrs[1], PubKeys[1] + res := sh(input.Ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.True(t, res.IsOK()) + res = sh(input.Ctx, NewTestMsgCreateValidator(addr1, val1, amt)) + require.True(t, res.IsOK()) + staking.EndBlocker(input.Ctx, input.StakingKeeper) + + rewardPolicy := input.TreasuryKeeper.RewardPolicy(input.Ctx) + input.TreasuryKeeper.UpdateRewardPolicy(input.Ctx) + rewardWeight := input.TreasuryKeeper.GetRewardWeight(input.Ctx, core.GetEpoch(input.Ctx)) + require.Equal(t, types.DefaultRewardWeight.Add(rewardPolicy.ChangeRateMax), rewardWeight) +} + +func TestUpdateTaxCap(t *testing.T) { + input := CreateTestInput(t) + input.SupplyKeeper.SetSupply(input.Ctx, + input.SupplyKeeper.GetSupply(input.Ctx).SetTotal( + sdk.NewCoins( + sdk.NewInt64Coin(core.MicroLunaDenom, 1000000), + sdk.NewInt64Coin(core.MicroSDRDenom, 1000000), + sdk.NewInt64Coin(core.MicroKRWDenom, 1000000), + ), + ), + ) + + // Create Validators + sdrPrice := sdk.NewDecWithPrec(13, 1) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdrPrice) + krwPrice := sdk.NewDecWithPrec(153412, 2) + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroKRWDenom, krwPrice) + input.TreasuryKeeper.UpdateTaxCap(input.Ctx) + + krwCap := input.TreasuryKeeper.GetTaxCap(input.Ctx, core.MicroKRWDenom) + sdrCapAmt := input.TreasuryKeeper.GetParams(input.Ctx).TaxPolicy.Cap.Amount + require.Equal(t, krwCap, krwPrice.Quo(sdrPrice).MulInt(sdrCapAmt).TruncateInt()) +} diff --git a/x/treasury/internal/keeper/querier.go b/x/treasury/internal/keeper/querier.go new file mode 100644 index 000000000..b23e689fe --- /dev/null +++ b/x/treasury/internal/keeper/querier.go @@ -0,0 +1,172 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/treasury/internal/types" +) + +// NewQuerier is the module level router for state queries +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case types.QueryCurrentEpoch: + return queryCurrentEpoch(ctx, keeper) + case types.QueryTaxRate: + return queryTaxRate(ctx, req, keeper) + case types.QueryTaxCap: + return queryTaxCap(ctx, req, keeper) + case types.QueryRewardWeight: + return queryRewardWeight(ctx, req, keeper) + case types.QuerySeigniorageProceeds: + return querySeigniorageProceeds(ctx, req, keeper) + case types.QueryTaxProceeds: + return queryTaxProceeds(ctx, req, keeper) + case types.QueryHistoricalIssuance: + return queryHistoricalIssuance(ctx, req, keeper) + case types.QueryParameters: + return queryParameters(ctx, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown market query endpoint") + } + } +} + +func queryCurrentEpoch(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { + curEpoch := core.GetEpoch(ctx) + bz, err := codec.MarshalJSONIndent(keeper.cdc, curEpoch) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryTaxRate(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryTaxRateParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + curEpoch := core.GetEpoch(ctx) + if 0 > params.Epoch || curEpoch < params.Epoch { + return nil, types.ErrInvalidEpoch(types.DefaultCodespace, curEpoch, params.Epoch) + } + + taxRate := keeper.GetTaxRate(ctx, params.Epoch) + bz, err := codec.MarshalJSONIndent(keeper.cdc, taxRate) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} + +func queryTaxCap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryTaxCapParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + taxCap := keeper.GetTaxCap(ctx, params.Denom) + bz, err := codec.MarshalJSONIndent(keeper.cdc, taxCap) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} + +func queryRewardWeight(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryRewardWeightParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + curEpoch := core.GetEpoch(ctx) + if 0 > params.Epoch || curEpoch < params.Epoch { + return nil, types.ErrInvalidEpoch(types.DefaultCodespace, curEpoch, params.Epoch) + } + + taxRate := keeper.GetRewardWeight(ctx, params.Epoch) + bz, err := codec.MarshalJSONIndent(keeper.cdc, taxRate) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} + +func querySeigniorageProceeds(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QuerySeigniorageProceedsParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + curEpoch := core.GetEpoch(ctx) + if 0 > params.Epoch || curEpoch < params.Epoch { + return nil, types.ErrInvalidEpoch(types.DefaultCodespace, curEpoch, params.Epoch) + } + + seigniorage := keeper.PeekEpochSeigniorage(ctx, params.Epoch) + bz, err := codec.MarshalJSONIndent(keeper.cdc, seigniorage) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryTaxProceeds(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryTaxProceedsParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + curEpoch := core.GetEpoch(ctx) + if 0 > params.Epoch || curEpoch < params.Epoch { + return nil, types.ErrInvalidEpoch(types.DefaultCodespace, curEpoch, params.Epoch) + } + + proceeds := keeper.PeekTaxProceeds(ctx, params.Epoch) + bz, err := codec.MarshalJSONIndent(keeper.cdc, proceeds) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +func queryHistoricalIssuance(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryHistoricalIssuanceParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + curEpoch := core.GetEpoch(ctx) + if 0 > params.Epoch || curEpoch < params.Epoch { + return nil, types.ErrInvalidEpoch(types.DefaultCodespace, curEpoch, params.Epoch) + } + + issuance := keeper.GetHistoricalIssuance(ctx, params.Epoch) + bz, err := codec.MarshalJSONIndent(keeper.cdc, issuance) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} + +func queryParameters(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) { + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} diff --git a/x/treasury/internal/keeper/querier_test.go b/x/treasury/internal/keeper/querier_test.go new file mode 100644 index 000000000..c3b5da48a --- /dev/null +++ b/x/treasury/internal/keeper/querier_test.go @@ -0,0 +1,303 @@ +package keeper + +import ( + "strings" + "testing" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/treasury/internal/types" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const custom = "custom" + +func getQueriedTaxRate(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch int64) sdk.Dec { + params := types.QueryTaxRateParams{ + Epoch: epoch, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTaxRate}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QueryTaxRate}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Dec + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedTaxCap(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, denom string) sdk.Int { + params := types.QueryTaxCapParams{ + Denom: denom, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTaxCap}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QueryTaxCap}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Int + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedRewardWeight(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch int64) sdk.Dec { + params := types.QueryRewardWeightParams{ + Epoch: epoch, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryRewardWeight}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QueryRewardWeight}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Dec + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedTaxProceeds(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch int64) sdk.Coins { + params := types.QueryTaxProceedsParams{ + Epoch: epoch, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTaxProceeds}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QueryTaxProceeds}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Coins + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedSeigniorageProceeds(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch int64) sdk.Int { + params := types.QuerySeigniorageProceedsParams{ + Epoch: epoch, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QuerySeigniorageProceeds}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QuerySeigniorageProceeds}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Int + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedCurrentEpoch(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) int64 { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryCurrentEpoch}, "/"), + Data: []byte{}, + } + + bz, err := querier(ctx, []string{types.QueryCurrentEpoch}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response int64 + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedHistoricalIssuance(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch int64) sdk.Coins { + params := types.QueryHistoricalIssuanceParams{ + Epoch: epoch, + } + + bz, err := cdc.MarshalJSON(params) + require.NoError(t, err) + + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryHistoricalIssuance}, "/"), + Data: bz, + } + + bz, err = querier(ctx, []string{types.QueryHistoricalIssuance}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var response sdk.Coins + err2 := cdc.UnmarshalJSON(bz, &response) + require.Nil(t, err2) + + return response +} + +func getQueriedParameters(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) types.Params { + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParameters}, "/"), + Data: []byte{}, + } + + bz, err := querier(ctx, []string{types.QueryParameters}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var params types.Params + err2 := cdc.UnmarshalJSON(bz, ¶ms) + require.Nil(t, err2) + + return params +} + +func TestQueryParams(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + params := types.DefaultParams() + input.TreasuryKeeper.SetParams(input.Ctx, params) + + queriedParams := getQueriedParameters(t, input.Ctx, input.Cdc, querier) + + require.Equal(t, queriedParams, params) +} + +func TestQueryRewardWeight(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + rewardWeight := sdk.NewDecWithPrec(77, 2) + input.TreasuryKeeper.SetRewardWeight(input.Ctx, rewardWeight) + + queriedRewardWeight := getQueriedRewardWeight(t, input.Ctx, input.Cdc, querier, core.GetEpoch(input.Ctx)) + + require.Equal(t, queriedRewardWeight, rewardWeight) +} + +func TestQueryTaxRate(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + taxRate := sdk.NewDecWithPrec(1, 3) + input.TreasuryKeeper.SetTaxRate(input.Ctx, taxRate) + + queriedTaxRate := getQueriedTaxRate(t, input.Ctx, input.Cdc, querier, core.GetEpoch(input.Ctx)) + + require.Equal(t, queriedTaxRate, taxRate) +} + +func TestQueryTaxCap(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + params := input.TreasuryKeeper.GetParams(input.Ctx) + + // Get a currency super random; should default to policy coin. + queriedTaxCap := getQueriedTaxCap(t, input.Ctx, input.Cdc, querier, "hello") + + require.Equal(t, queriedTaxCap, params.TaxPolicy.Cap.Amount) +} + +func TestQueryCurrentEpoch(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + curEpoch := core.GetEpoch(input.Ctx) + + queriedCurEpoch := getQueriedCurrentEpoch(t, input.Ctx, input.Cdc, querier) + + require.Equal(t, queriedCurEpoch, curEpoch) +} + +func TestQueryTaxProceeds(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + taxProceeds := sdk.Coins{ + sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000).MulRaw(core.MicroUnit)), + } + input.TreasuryKeeper.RecordTaxProceeds(input.Ctx, taxProceeds) + + queriedTaxProceeds := getQueriedTaxProceeds(t, input.Ctx, input.Cdc, querier, core.GetEpoch(input.Ctx)) + + require.Equal(t, queriedTaxProceeds, taxProceeds) +} + +func TestQuerySeigniorageProceeds(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + targetIssuance := sdk.NewInt(1000) + targetSeigniorage := sdk.NewInt(10) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, targetIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, targetIssuance.Sub(targetSeigniorage)))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + queriedSeigniorageProceeds := getQueriedSeigniorageProceeds(t, input.Ctx, input.Cdc, querier, core.GetEpoch(input.Ctx)) + + require.Equal(t, targetSeigniorage, queriedSeigniorageProceeds) +} + +func TestQueryHistoricalIssuance(t *testing.T) { + input := CreateTestInput(t) + querier := NewQuerier(input.TreasuryKeeper) + + targetIssuance := sdk.NewInt(1000) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, targetIssuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + queriedHistoricalIssuance := getQueriedHistoricalIssuance(t, input.Ctx, input.Cdc, querier, core.GetEpoch(input.Ctx)).AmountOf(core.MicroLunaDenom) + + require.Equal(t, targetIssuance, queriedHistoricalIssuance) +} diff --git a/x/treasury/internal/keeper/seigniorage.go b/x/treasury/internal/keeper/seigniorage.go new file mode 100644 index 000000000..462a45d3c --- /dev/null +++ b/x/treasury/internal/keeper/seigniorage.go @@ -0,0 +1,62 @@ +package keeper + +import ( + "fmt" + + "github.com/terra-project/core/x/treasury/internal/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + core "github.com/terra-project/core/types" +) + +// SettleSeigniorage +func (k Keeper) SettleSeigniorage(ctx sdk.Context) { + // Mint seigniorage for oracle and community pool + epoch := core.GetEpoch(ctx) + seigniorageLunaAmt := k.PeekEpochSeigniorage(ctx, epoch) + if seigniorageLunaAmt.LTE(sdk.ZeroInt()) { + return + } + + // Settle current epoch seigniorage + rewardWeight := k.GetRewardWeight(ctx, epoch) + + // Align seigniorage to usdr + seigniorageLunaDecCoin := sdk.NewDecCoin(core.MicroLunaDenom, seigniorageLunaAmt) + seigniorageDecCoin, err := k.marketKeeper.GetSwapDecCoin(ctx, seigniorageLunaDecCoin, core.MicroSDRDenom) + if err != nil { + k.Logger(ctx).Error(fmt.Sprintf("[Treasury] Failed to swap seigniorage to usdr, %s", err.Error())) + return + } + + // Mint seigniorage + seigniorageCoin, _ := seigniorageDecCoin.TruncateDecimal() + seigniorageCoins := sdk.NewCoins(seigniorageCoin) + err = k.supplyKeeper.MintCoins(ctx, types.ModuleName, seigniorageCoins) + if err != nil { + panic(err) + } + seigniorageAmt := seigniorageCoin.Amount + + // Send reward to oracle module + oracleRewardAmt := rewardWeight.MulInt(seigniorageAmt).TruncateInt() + oracleRewardCoins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, oracleRewardAmt)) + err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.oracleModuleName, oracleRewardCoins) + if err != nil { + panic(err) + } + + // Send left to distribution module + leftAmt := seigniorageAmt.Sub(oracleRewardAmt) + leftCoins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, leftAmt)) + err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.distributionModuleName, leftCoins) + if err != nil { + panic(err) + } + + // Update distribution community p9ol + feePool := k.distrKeeper.GetFeePool(ctx) + feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoins(leftCoins)) + k.distrKeeper.SetFeePool(ctx, feePool) +} diff --git a/x/treasury/internal/keeper/seigniorage_test.go b/x/treasury/internal/keeper/seigniorage_test.go new file mode 100644 index 000000000..d21c76779 --- /dev/null +++ b/x/treasury/internal/keeper/seigniorage_test.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "fmt" + "math/rand" + "testing" + + core "github.com/terra-project/core/types" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestSettle(t *testing.T) { + input := CreateTestInput(t) + + input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdk.OneDec()) + + issuance := sdk.NewInt(rand.Int63() + 1) + supply := input.SupplyKeeper.GetSupply(input.Ctx) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, issuance))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + input.TreasuryKeeper.UpdateIssuance(input.Ctx) + + input.Ctx = input.Ctx.WithBlockHeight(core.BlocksPerEpoch) + supply = supply.SetTotal(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.ZeroInt()))) + input.SupplyKeeper.SetSupply(input.Ctx, supply) + + // check seigniorage update + require.Equal(t, issuance, input.TreasuryKeeper.PeekEpochSeigniorage(input.Ctx, 1)) + + input.TreasuryKeeper.SettleSeigniorage(input.Ctx) + oracleAcc := input.SupplyKeeper.GetModuleAccount(input.Ctx, input.TreasuryKeeper.oracleModuleName) + feePool := input.DistrKeeper.GetFeePool(input.Ctx) + fmt.Println(oracleAcc) + fmt.Println(feePool) + + rewardWeight := input.TreasuryKeeper.GetRewardWeight(input.Ctx, 1) + oracleRewardAmt := rewardWeight.MulInt(issuance).TruncateInt() + leftAmt := issuance.Sub(oracleRewardAmt) + + require.Equal(t, oracleRewardAmt, oracleAcc.GetCoins().AmountOf(core.MicroSDRDenom)) + require.Equal(t, leftAmt, feePool.CommunityPool.AmountOf(core.MicroSDRDenom).TruncateInt()) +} diff --git a/x/treasury/internal/keeper/test_utils.go b/x/treasury/internal/keeper/test_utils.go new file mode 100644 index 000000000..92029a18a --- /dev/null +++ b/x/treasury/internal/keeper/test_utils.go @@ -0,0 +1,222 @@ +// nolint:deadcode unused noalias +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + + core "github.com/terra-project/core/types" + "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/oracle" + "github.com/terra-project/core/x/treasury/internal/types" + + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +var ( + PubKeys = []crypto.PubKey{ + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + secp256k1.GenPrivKey().PubKey(), + } + + Addrs = []sdk.AccAddress{ + sdk.AccAddress(PubKeys[0].Address()), + sdk.AccAddress(PubKeys[1].Address()), + sdk.AccAddress(PubKeys[2].Address()), + } + + ValAddrs = []sdk.ValAddress{ + sdk.ValAddress(PubKeys[0].Address()), + sdk.ValAddress(PubKeys[1].Address()), + sdk.ValAddress(PubKeys[2].Address()), + } + + InitTokens = sdk.TokensFromConsensusPower(200) + InitCoins = sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens)) +) + +// TestInput nolint +type TestInput struct { + Ctx sdk.Context + Cdc *codec.Codec + TreasuryKeeper Keeper + StakingKeeper staking.Keeper + OracleKeeper oracle.Keeper + SupplyKeeper supply.Keeper + MarketKeeper market.Keeper + DistrKeeper distr.Keeper +} + +func newTestCodec() *codec.Codec { + cdc := codec.New() + + types.RegisterCodec(cdc) + market.RegisterCodec(cdc) + oracle.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + supply.RegisterCodec(cdc) + staking.RegisterCodec(cdc) + distr.RegisterCodec(cdc) + params.RegisterCodec(cdc) + + return cdc +} + +// CreateTestInput nolint +func CreateTestInput(t *testing.T) TestInput { + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) + keyDistr := sdk.NewKVStoreKey(distr.StoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + keyMarket := sdk.NewKVStoreKey(market.StoreKey) + keyTreasury := sdk.NewKVStoreKey(types.StoreKey) + + cdc := newTestCodec() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) + + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyTreasury, sdk.StoreTypeIAVL, db) + + require.NoError(t, ms.LoadLatestVersion()) + + blackListAddrs := map[string]bool{ + auth.FeeCollectorName: true, + staking.NotBondedPoolName: true, + staking.BondedPoolName: true, + distr.ModuleName: true, + oracle.ModuleName: true, + market.ModuleName: true, + types.ModuleName: true, + } + + paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams, params.DefaultCodespace) + accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) + bankKeeper := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blackListAddrs) + + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + distr.ModuleName: nil, + market.ModuleName: {supply.Burner, supply.Minter}, + oracle.ModuleName: nil, + types.ModuleName: {supply.Minter}, + } + + supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) + totalSupply := sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens.MulRaw(int64(len(Addrs))))) + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + stakingKeeper := staking.NewKeeper( + cdc, + keyStaking, tKeyStaking, + supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), + staking.DefaultCodespace, + ) + + distrKeeper := distr.NewKeeper( + cdc, + keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), + stakingKeeper, supplyKeeper, distr.DefaultCodespace, + auth.FeeCollectorName, blackListAddrs, + ) + + distrKeeper.SetFeePool(ctx, distr.InitialFeePool()) + distrKeeper.SetCommunityTax(ctx, sdk.NewDecWithPrec(2, 2)) + distrKeeper.SetBaseProposerReward(ctx, sdk.NewDecWithPrec(1, 2)) + distrKeeper.SetBonusProposerReward(ctx, sdk.NewDecWithPrec(4, 2)) + + oracleKeeper := oracle.NewKeeper( + cdc, + keyOracle, paramsKeeper.Subspace(oracle.DefaultParamspace), + distrKeeper, stakingKeeper, supplyKeeper, distr.ModuleName, + oracle.DefaultCodespace, + ) + + marketKeeper := market.NewKeeper( + cdc, + keyMarket, paramsKeeper.Subspace(market.DefaultParamspace), + oracleKeeper, supplyKeeper, market.DefaultCodespace, + ) + + treasuryKeeper := NewKeeper( + cdc, + keyTreasury, paramsKeeper.Subspace(types.DefaultParamspace), + supplyKeeper, marketKeeper, stakingKeeper, distrKeeper, + oracle.ModuleName, distr.ModuleName, + types.DefaultCodespace, + ) + + treasuryKeeper.SetParams(ctx, types.DefaultParams()) + + feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) + distrAcc := supply.NewEmptyModuleAccount(distr.ModuleName) + marketAcc := supply.NewEmptyModuleAccount(market.ModuleName, supply.Burner, supply.Minter) + oracleAcc := supply.NewEmptyModuleAccount(oracle.ModuleName, supply.Minter) + + notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, InitTokens.MulRaw(int64(len(Addrs)))))) + + supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) + supplyKeeper.SetModuleAccount(ctx, bondPool) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + supplyKeeper.SetModuleAccount(ctx, distrAcc) + supplyKeeper.SetModuleAccount(ctx, marketAcc) + supplyKeeper.SetModuleAccount(ctx, oracleAcc) + + genesis := staking.DefaultGenesisState() + genesis.Params.BondDenom = core.MicroLunaDenom + _ = staking.InitGenesis(ctx, stakingKeeper, accountKeeper, supplyKeeper, genesis) + + for _, addr := range Addrs { + _, err := bankKeeper.AddCoins(ctx, sdk.AccAddress(addr), InitCoins) + require.NoError(t, err) + } + + stakingKeeper.SetHooks(staking.NewMultiStakingHooks(distrKeeper.Hooks(), oracleKeeper.Hooks())) + + return TestInput{ctx, cdc, treasuryKeeper, stakingKeeper, oracleKeeper, supplyKeeper, marketKeeper, distrKeeper} +} + +func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator { + commission := staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + return staking.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(core.MicroLunaDenom, amt), + staking.Description{}, commission, sdk.OneInt(), + ) +} diff --git a/x/treasury/internal/test_common.go b/x/treasury/internal/test_common.go new file mode 100644 index 000000000..5bf0569ce --- /dev/null +++ b/x/treasury/internal/test_common.go @@ -0,0 +1 @@ +package internal diff --git a/x/treasury/internal/types/codec.go b/x/treasury/internal/types/codec.go new file mode 100644 index 000000000..b53d07d50 --- /dev/null +++ b/x/treasury/internal/types/codec.go @@ -0,0 +1,18 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { +} + +// generic sealed codec to be used throughout module +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/treasury/constraint.go b/x/treasury/internal/types/constraint.go similarity index 98% rename from x/treasury/constraint.go rename to x/treasury/internal/types/constraint.go index b83d2cca9..26fdc2732 100644 --- a/x/treasury/constraint.go +++ b/x/treasury/internal/types/constraint.go @@ -1,4 +1,4 @@ -package treasury +package types import ( "fmt" diff --git a/x/treasury/internal/types/constraint_test.go b/x/treasury/internal/types/constraint_test.go new file mode 100644 index 000000000..b0851db1c --- /dev/null +++ b/x/treasury/internal/types/constraint_test.go @@ -0,0 +1,35 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestConstraintClamp(t *testing.T) { + taxPolicy := DefaultTaxPolicy + prevRate := DefaultTaxRate + + // Case 1: try to update delta > maxUpdateRate + newRate := prevRate.Add(taxPolicy.ChangeRateMax.MulInt64(2)) + clampedRate := taxPolicy.Clamp(prevRate, newRate) + require.Equal(t, prevRate.Add(taxPolicy.ChangeRateMax), clampedRate) + + // Case 2: try to update delta > maxUpdateRate in other direction + newRate = prevRate.Sub(taxPolicy.ChangeRateMax.MulInt64(2)) + clampedRate = taxPolicy.Clamp(prevRate, newRate) + require.Equal(t, prevRate.Sub(taxPolicy.ChangeRateMax), clampedRate) + + // Case 3: try to update the new rate > maxRate + prevRate = taxPolicy.RateMax + newRate = taxPolicy.RateMax.Add(sdk.NewDecWithPrec(1, 3)) + clampedRate = taxPolicy.Clamp(prevRate, newRate) + require.Equal(t, taxPolicy.RateMax, clampedRate) + + // Case 4: try to update the new rate < minRate + prevRate = taxPolicy.RateMin + newRate = taxPolicy.RateMin.Sub(sdk.NewDecWithPrec(1, 3)) + clampedRate = taxPolicy.Clamp(prevRate, newRate) + require.Equal(t, taxPolicy.RateMin, clampedRate) +} diff --git a/x/treasury/internal/types/errors.go b/x/treasury/internal/types/errors.go new file mode 100644 index 000000000..f91345043 --- /dev/null +++ b/x/treasury/internal/types/errors.go @@ -0,0 +1,22 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Oracle error codes +const ( + DefaultCodespace sdk.CodespaceType = "treasury" + + CodeInvalidEpoch sdk.CodeType = 1 +) + +// ---------------------------------------- +// Error constructors + +// ErrInvalidEpoch called when the denom is not known +func ErrInvalidEpoch(codespace sdk.CodespaceType, curEpoch, epoch int64) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEpoch, fmt.Sprintf("The query epoch should be between [0, %d] but given %d", curEpoch, epoch)) +} diff --git a/x/treasury/internal/types/events.go b/x/treasury/internal/types/events.go new file mode 100644 index 000000000..be29a001c --- /dev/null +++ b/x/treasury/internal/types/events.go @@ -0,0 +1,11 @@ +//noalias +package types + +// Treasury module event types +const ( + EventTypePolichUpdate = "policy_update" + + AttributeKeyTax = "tax" + AttributeKeyReward = "reward" + AttributeKeyTaxCap = "tax_cap" +) diff --git a/x/treasury/internal/types/exptected_keepers.go b/x/treasury/internal/types/exptected_keepers.go new file mode 100644 index 000000000..d892809a0 --- /dev/null +++ b/x/treasury/internal/types/exptected_keepers.go @@ -0,0 +1,31 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// expected supply keeper +type SupplyKeeper interface { + GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) sdk.Error +} + +// expected market keeper +type MarketKeeper interface { + GetSwapDecCoin(ctx sdk.Context, offerCoin sdk.DecCoin, askDenom string) (sdk.DecCoin, sdk.Error) + GetSwapCoin(ctx sdk.Context, offerCoin sdk.Coin, askDenom string, isInternal bool) (retCoin sdk.Coin, spread sdk.Dec, err sdk.Error) +} + +// expected keeper for staking module +type StakingKeeper interface { + TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set +} + +// expected keeper for distribution module +type DistributionKeeper interface { + GetFeePool(ctx sdk.Context) (feePool distrtypes.FeePool) + SetFeePool(ctx sdk.Context, feePool distrtypes.FeePool) +} diff --git a/x/treasury/internal/types/genesis.go b/x/treasury/internal/types/genesis.go new file mode 100644 index 000000000..f094956ee --- /dev/null +++ b/x/treasury/internal/types/genesis.go @@ -0,0 +1,60 @@ +package types + +import ( + "bytes" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState - all market state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` // market params + TaxRate sdk.Dec `json:"tax_rate" yaml:"tax_rate"` + RewardWeight sdk.Dec `json:"reward_weight" yaml:"reward_weight"` +} + +// NewGenesisState creates a new GenesisState object +func NewGenesisState(params Params, taxRate sdk.Dec, rewardWeight sdk.Dec) GenesisState { + return GenesisState{ + Params: params, + TaxRate: taxRate, + RewardWeight: rewardWeight, + } +} + +// get raw genesis raw message for testing +func DefaultGenesisState() GenesisState { + return GenesisState{ + Params: DefaultParams(), + TaxRate: DefaultTaxRate, + RewardWeight: DefaultRewardWeight, + } +} + +// ValidateGenesis validates the provided oracle genesis state to ensure the +// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) +func ValidateGenesis(data GenesisState) error { + if data.TaxRate.LT(data.Params.TaxPolicy.RateMin) || data.TaxRate.GT(data.Params.TaxPolicy.RateMax) { + return fmt.Errorf("tax-rate must less than RateMax(%s) and bigger than RateMin(%s)", data.Params.TaxPolicy.RateMax, data.Params.TaxPolicy.RateMin) + } + + if data.RewardWeight.LT(data.Params.RewardPolicy.RateMin) || data.RewardWeight.GT(data.Params.RewardPolicy.RateMax) { + return fmt.Errorf("reward-weight must less than WeightMax(%s) and bigger than RateMin(%s)", data.Params.RewardPolicy.RateMax, data.Params.RewardPolicy.RateMin) + } + + return data.Params.Validate() +} + +// Checks whether 2 GenesisState structs are equivalent. +func (data GenesisState) Equal(data2 GenesisState) bool { + b1 := ModuleCdc.MustMarshalBinaryBare(data) + b2 := ModuleCdc.MustMarshalBinaryBare(data2) + return bytes.Equal(b1, b2) +} + +// Returns if a GenesisState is empty or has data in it +func (data GenesisState) IsEmpty() bool { + emptyGenState := GenesisState{} + return data.Equal(emptyGenState) +} diff --git a/x/treasury/internal/types/genesis_test.go b/x/treasury/internal/types/genesis_test.go new file mode 100644 index 000000000..c5122dcb1 --- /dev/null +++ b/x/treasury/internal/types/genesis_test.go @@ -0,0 +1,33 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGenesisValidation(t *testing.T) { + genState := DefaultGenesisState() + require.NoError(t, ValidateGenesis(genState)) + + genState.TaxRate = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) + + genState.TaxRate = sdk.NewDecWithPrec(5, 2) + genState.RewardWeight = sdk.NewDec(-1) + require.Error(t, ValidateGenesis(genState)) +} + +func TestGenesisEqual(t *testing.T) { + genState1 := DefaultGenesisState() + genState2 := DefaultGenesisState() + + require.True(t, genState1.Equal(genState2)) +} + +func TestGenesisEmpty(t *testing.T) { + genState := GenesisState{} + require.True(t, genState.IsEmpty()) +} diff --git a/x/treasury/internal/types/keys.go b/x/treasury/internal/types/keys.go new file mode 100644 index 000000000..69c88e120 --- /dev/null +++ b/x/treasury/internal/types/keys.go @@ -0,0 +1,73 @@ +package types + +import ( + "encoding/binary" +) + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "treasury" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // RouterKey is the message route for treasury + RouterKey = ModuleName + + // QuerierRoute is the querier route for treasury + QuerierRoute = ModuleName +) + +// Keys for treasury store +// Items are stored with the following key: values +// +// - 0x01: sdk.Dec +// +// - 0x02: sdk.Dec +// +// - 0x03: sdk.Int +// +// - 0x04: sdk.Coins +// +// - 0x05: sdk.Coins +var ( + // Keys for store prefixes + TaxRateKey = []byte{0x01} // prefix for each key to a tax-rate + RewardWeightKey = []byte{0x02} // prefix for each key to a reward-weight + TaxCapKey = []byte{0x03} // prefix for each key to a tax-cap + TaxProceedsKey = []byte{0x04} // prefix for each key to a tax-proceeds + HistoricalIssuanceKey = []byte{0x05} // prefix for each key to a historical issuance +) + +// GetTaxRateKey - stored by *epoch* +func GetTaxRateKey(epoch int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(epoch)) + return append(TaxRateKey, b...) +} + +// GetRewardWeightKey - stored by *epoch* +func GetRewardWeightKey(epoch int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(epoch)) + return append(RewardWeightKey, b...) +} + +// GetTaxCapKey - stored by *denom* +func GetTaxCapKey(denom string) []byte { + return append(TaxCapKey, []byte(denom)...) +} + +// GetTaxProceedsKey - stored by *epoch* +func GetTaxProceedsKey(epoch int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(epoch)) + return append(TaxProceedsKey, b...) +} + +// GetHistoricalIssuanceKey - stored by *epoch* +func GetHistoricalIssuanceKey(epoch int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(epoch)) + return append(HistoricalIssuanceKey, b...) +} diff --git a/x/treasury/internal/types/params.go b/x/treasury/internal/types/params.go new file mode 100644 index 000000000..fa43446e3 --- /dev/null +++ b/x/treasury/internal/types/params.go @@ -0,0 +1,126 @@ +package types + +import ( + "fmt" + + core "github.com/terra-project/core/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/subspace" +) + +// DefaultParamspace +const DefaultParamspace = ModuleName + +// Parameter keys +var ( + ParamStoreKeyTaxPolicy = []byte("taxpolicy") + ParamStoreKeyRewardPolicy = []byte("rewardpolicy") + ParamStoreKeySeigniorageBurdenTarget = []byte("seigniorageburdentarget") + ParamStoreKeyMiningIncrement = []byte("miningincrement") + ParamStoreKeyWindowShort = []byte("windowshort") + ParamStoreKeyWindowLong = []byte("windowlong") + ParamStoreKeyWindowProbation = []byte("windowprobation") +) + +// Default parameter values +var ( + DefaultTaxPolicy = PolicyConstraints{ + RateMin: sdk.NewDecWithPrec(5, 4), // 0.05% + RateMax: sdk.NewDecWithPrec(1, 2), // 1% + Cap: sdk.NewCoin(core.MicroSDRDenom, sdk.OneInt().MulRaw(core.MicroUnit)), // 1 SDR Tax cap + ChangeRateMax: sdk.NewDecWithPrec(25, 5), // 0.025% + } + DefaultRewardPolicy = PolicyConstraints{ + RateMin: sdk.NewDecWithPrec(5, 2), // 5% + RateMax: sdk.NewDecWithPrec(90, 2), // 90% + ChangeRateMax: sdk.NewDecWithPrec(25, 3), // 2.5% + Cap: sdk.NewCoin("unused", sdk.ZeroInt()), // UNUSED + } + DefaultSeigniorageBurdenTarget = sdk.NewDecWithPrec(67, 2) // 67% + DefaultMiningIncrement = sdk.NewDecWithPrec(107, 2) // 1.07 mining increment; exponential growth + DefaultWindowShort = int64(4) // a month + DefaultWindowLong = int64(52) // a year + DefaultWindowProbation = int64(12) // 3 month + DefaultTaxRate = sdk.NewDecWithPrec(1, 3) // 0.1% + DefaultRewardWeight = sdk.NewDecWithPrec(5, 2) // 5% +) + +var _ subspace.ParamSet = &Params{} + +// Params treasury parameters +type Params struct { + TaxPolicy PolicyConstraints `json:"tax_policy" yaml:"tax_policy"` + RewardPolicy PolicyConstraints `json:"reward_policy" yaml:"reward_policy"` + SeigniorageBurdenTarget sdk.Dec `json:"seigniorage_burden_target" yaml:"seigniorage_burden_target"` + MiningIncrement sdk.Dec `json:"mining_increment" yaml:"mining_increment"` + WindowShort int64 `json:"window_short" yaml:"window_short"` + WindowLong int64 `json:"window_long" yaml:"window_long"` + WindowProbation int64 `json:"window_probation" yaml:"window_probation"` +} + +// DefaultParams creates default treasury module parameters +func DefaultParams() Params { + return Params{ + TaxPolicy: DefaultTaxPolicy, + RewardPolicy: DefaultRewardPolicy, + SeigniorageBurdenTarget: DefaultSeigniorageBurdenTarget, + MiningIncrement: DefaultMiningIncrement, + WindowShort: DefaultWindowShort, + WindowLong: DefaultWindowLong, + WindowProbation: DefaultWindowProbation, + } +} + +// Validate params +func (params Params) Validate() error { + if params.TaxPolicy.RateMax.LT(params.TaxPolicy.RateMin) { + return fmt.Errorf("treasury TaxPolicy.RateMax %s must be greater than TaxPolicy.RateMin %s", + params.TaxPolicy.RateMax.String(), params.TaxPolicy.RateMin.String()) + } + + if params.TaxPolicy.RateMin.IsNegative() { + return fmt.Errorf("treasury parameter TaxPolicy.RateMin must be >= 0, is %s", params.TaxPolicy.RateMin.String()) + } + + if params.RewardPolicy.RateMax.LT(params.RewardPolicy.RateMin) { + return fmt.Errorf("treasury RewardPolicy.RateMax %s must be greater than RewardPolicy.RateMin %s", + params.RewardPolicy.RateMax.String(), params.RewardPolicy.RateMin.String()) + } + + if params.RewardPolicy.RateMin.IsNegative() { + return fmt.Errorf("treasury parameter RewardPolicy.RateMin must be >= 0, is %s", params.RewardPolicy.RateMin.String()) + } + + return nil +} + +// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs +// pairs of treasury module's parameters. +// nolint +func (params *Params) ParamSetPairs() subspace.ParamSetPairs { + return subspace.ParamSetPairs{ + {Key: ParamStoreKeyTaxPolicy, Value: ¶ms.TaxPolicy}, + {Key: ParamStoreKeyRewardPolicy, Value: ¶ms.RewardPolicy}, + {Key: ParamStoreKeySeigniorageBurdenTarget, Value: ¶ms.SeigniorageBurdenTarget}, + {Key: ParamStoreKeyMiningIncrement, Value: ¶ms.MiningIncrement}, + {Key: ParamStoreKeyWindowShort, Value: ¶ms.WindowShort}, + {Key: ParamStoreKeyWindowLong, Value: ¶ms.WindowLong}, + {Key: ParamStoreKeyWindowProbation, Value: ¶ms.WindowProbation}, + } +} + +// implements fmt.Stringer +func (params Params) String() string { + return fmt.Sprintf(`Treasury Params: + Tax Policy : { %v } + Reward Policy : { %v } + + SeigniorageBurdenTarget : %v + MiningIncrement : %v + + WindowShort : %v + WindowLong : %v + `, params.TaxPolicy, params.RewardPolicy, params.SeigniorageBurdenTarget, + params.MiningIncrement, params.WindowShort, params.WindowLong) +} diff --git a/x/treasury/internal/types/params_test.go b/x/treasury/internal/types/params_test.go new file mode 100644 index 000000000..6b81e3978 --- /dev/null +++ b/x/treasury/internal/types/params_test.go @@ -0,0 +1,33 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestParams(t *testing.T) { + params := DefaultParams() + require.NoError(t, params.Validate()) + + params = DefaultParams() + params.TaxPolicy.RateMax = sdk.ZeroDec() + require.Error(t, params.Validate()) + + params = DefaultParams() + params.TaxPolicy.RateMin = sdk.NewDec(-1) + require.Error(t, params.Validate()) + + params = DefaultParams() + params.RewardPolicy.RateMax = sdk.ZeroDec() + require.Error(t, params.Validate()) + + params = DefaultParams() + params.RewardPolicy.RateMin = sdk.NewDec(-1) + require.Error(t, params.Validate()) + + require.NotNil(t, params.ParamSetPairs()) + require.NotNil(t, params.String()) +} diff --git a/x/treasury/internal/types/querier.go b/x/treasury/internal/types/querier.go new file mode 100644 index 000000000..07520dc08 --- /dev/null +++ b/x/treasury/internal/types/querier.go @@ -0,0 +1,85 @@ +package types + +// query endpoints supported by the auth Querier +const ( + QueryCurrentEpoch = "currentEpoch" + QueryTaxRate = "taxRate" + QueryTaxCap = "taxCap" + QueryRewardWeight = "rewardWeight" + QuerySeigniorageProceeds = "seigniorageProceeds" + QueryTaxProceeds = "taxProceeds" + QueryParameters = "parameters" + QueryHistoricalIssuance = "historicalIssuance" +) + +// QueryTaxCapParams for query +// - 'custom/treasury/taxRate +type QueryTaxCapParams struct { + Denom string +} + +func NewQueryTaxCapParams(denom string) QueryTaxCapParams { + return QueryTaxCapParams{ + Denom: denom, + } +} + +// QueryTaxRateParams for query +// - 'custom/treasury/taxRate +type QueryTaxRateParams struct { + Epoch int64 +} + +func NewQueryTaxRateParams(epoch int64) QueryTaxRateParams { + return QueryTaxRateParams{ + Epoch: epoch, + } +} + +// QueryRewardWeightParams for query +// - 'custom/treasury/rewardWeight +type QueryRewardWeightParams struct { + Epoch int64 +} + +func NewQueryRewardWeightParams(epoch int64) QueryRewardWeightParams { + return QueryRewardWeightParams{ + Epoch: epoch, + } +} + +// QuerySeigniorageProceedsParams for query +// - 'custom/treasury/seigniorageProceeds +type QuerySeigniorageProceedsParams struct { + Epoch int64 +} + +func NewQuerySeigniorageParams(epoch int64) QuerySeigniorageProceedsParams { + return QuerySeigniorageProceedsParams{ + Epoch: epoch, + } +} + +// QueryTaxProceedsParams for query +// - 'custom/treasury/taxProceeds +type QueryTaxProceedsParams struct { + Epoch int64 +} + +func NewQueryTaxProceedsParams(epoch int64) QueryTaxProceedsParams { + return QueryTaxProceedsParams{ + Epoch: epoch, + } +} + +// QueryHistoricalIssuanceParams for query +// - 'custom/treasury/microLunaIssuance +type QueryHistoricalIssuanceParams struct { + Epoch int64 +} + +func NewQueryHistoricalIssuanceParams(epoch int64) QueryHistoricalIssuanceParams { + return QueryHistoricalIssuanceParams{ + Epoch: epoch, + } +} diff --git a/x/treasury/keeper.go b/x/treasury/keeper.go deleted file mode 100644 index 3f1820693..000000000 --- a/x/treasury/keeper.go +++ /dev/null @@ -1,173 +0,0 @@ -package treasury - -import ( - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper of the treasury store -type Keeper struct { - cdc *codec.Codec - key sdk.StoreKey - - valset sdk.ValidatorSet - - mtk MintKeeper - mk MarketKeeper - - paramSpace params.Subspace -} - -// NewKeeper constructs a new keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, valset sdk.ValidatorSet, - mtk MintKeeper, mk MarketKeeper, paramspace params.Subspace) Keeper { - return Keeper{ - cdc: cdc, - key: key, - valset: valset, - mtk: mtk, - mk: mk, - paramSpace: paramspace.WithKeyTable(paramKeyTable()), - } -} - -//----------------------------------- -// Reward weight logic - -// SetRewardWeight sets the ratio of the treasury that goes to mining rewards, i.e. -// supply of Luna that is burned. You can only set the reward weight of the current epoch. -func (k Keeper) SetRewardWeight(ctx sdk.Context, weight sdk.Dec) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(weight) - store.Set(keyRewardWeight(util.GetEpoch(ctx)), bz) -} - -// GetRewardWeight returns the mining reward weight -func (k Keeper) GetRewardWeight(ctx sdk.Context, epoch sdk.Int) (rewardWeight sdk.Dec) { - store := ctx.KVStore(k.key) - - if bz := store.Get(keyRewardWeight(epoch)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &rewardWeight) - return - } - - for e := epoch; e.GTE(sdk.ZeroInt()); e = e.Sub(sdk.OneInt()) { - if bz := store.Get(keyRewardWeight(e)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &rewardWeight) - break - } else if epoch.LTE(sdk.ZeroInt()) { - // Genesis epoch; nothing exists in store so we set to default state - rewardWeight = DefaultGenesisState().GenesisRewardWeight - break - } - } - - // Set reward weight to the store - bz := k.cdc.MustMarshalBinaryLengthPrefixed(rewardWeight) - store.Set(keyRewardWeight(epoch), bz) - - return -} - -//----------------------------------- -// Params logic - -// GetParams get treasury params from the global param store -func (k Keeper) GetParams(ctx sdk.Context) Params { - var params Params - k.paramSpace.Get(ctx, paramStoreKeyParams, ¶ms) - return params -} - -// SetParams set treasury params from the global param store -func (k Keeper) SetParams(ctx sdk.Context, params Params) { - k.paramSpace.Set(ctx, paramStoreKeyParams, ¶ms) -} - -//----------------------------------- -// Tax logic - -// SetTaxRate sets the tax rate; called from the treasury. -func (k Keeper) SetTaxRate(ctx sdk.Context, rate sdk.Dec) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(rate) - store.Set(keyTaxRate(util.GetEpoch(ctx)), bz) -} - -// GetTaxRate gets the tax rate -func (k Keeper) GetTaxRate(ctx sdk.Context, epoch sdk.Int) (rate sdk.Dec) { - store := ctx.KVStore(k.key) - if bz := store.Get(keyTaxRate(epoch)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &rate) - } else { - if epoch.LTE(sdk.ZeroInt()) { - rate = DefaultGenesisState().GenesisTaxRate - } else { - // Fetch the tax rate of the previous epoch - rate = k.GetTaxRate(ctx, epoch.Sub(sdk.OneInt())) - } - - // Set issuance to the store - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(rate) - store.Set(keyTaxRate(epoch), bz) - } - return -} - -// setTaxCap sets the Tax Cap. Denominated in integer units of the reference {denom} -func (k Keeper) setTaxCap(ctx sdk.Context, denom string, cap sdk.Int) { - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(cap) - store.Set(keyTaxCap(denom), bz) -} - -// GetTaxCap gets the Tax Cap. Denominated in integer units of the reference {denom} -func (k Keeper) GetTaxCap(ctx sdk.Context, denom string) (taxCap sdk.Int) { - store := ctx.KVStore(k.key) - - if bz := store.Get(keyTaxCap(denom)); bz != nil { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &taxCap) - } else { - // Tax cap does not exist for the asset; compute it by - // comparing it with the tax cap for TerraSDR - referenceCap := k.GetParams(ctx).TaxPolicy.Cap - reqCap, _, err := k.mk.GetSwapCoin(ctx, referenceCap, denom, true) - - // The coin is more valuable than TaxPolicy asset. just follow the Policy Cap. - if err != nil { - reqCap = sdk.NewCoin(denom, referenceCap.Amount) - } - - taxCap = reqCap.Amount - k.setTaxCap(ctx, denom, taxCap) - } - - return -} - -// RecordTaxProceeds add tax proceeds that have been added this epoch -func (k Keeper) RecordTaxProceeds(ctx sdk.Context, delta sdk.Coins) { - epoch := util.GetEpoch(ctx) - proceeds := k.PeekTaxProceeds(ctx, epoch) - proceeds = proceeds.Add(delta) - - store := ctx.KVStore(k.key) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(proceeds) - store.Set(keyTaxProceeds(epoch), bz) -} - -// PeekTaxProceeds peeks the total amount of taxes that have been collected in the given epoch. -func (k Keeper) PeekTaxProceeds(ctx sdk.Context, epoch sdk.Int) (res sdk.Coins) { - store := ctx.KVStore(k.key) - bz := store.Get(keyTaxProceeds(epoch)) - if bz == nil { - res = sdk.Coins{} - } else { - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) - } - return -} diff --git a/x/treasury/keeper_keys.go b/x/treasury/keeper_keys.go deleted file mode 100644 index f1cc6e4af..000000000 --- a/x/treasury/keeper_keys.go +++ /dev/null @@ -1,46 +0,0 @@ -package treasury - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// nolint -var ( - prefixRewardWeight = []byte("reward_weight") - PrefixClaim = []byte("claim") - paramStoreKeyParams = []byte("params") - - prefixTaxRate = []byte("tax_rate") - prefixTaxProceeds = []byte("tax_proceeds") - prefixTaxCap = []byte("tax_cap") - prefixIssuance = []byte("issuance") -) - -func keyTaxRate(epoch sdk.Int) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixTaxRate, epoch)) -} - -func keyRewardWeight(epoch sdk.Int) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixRewardWeight, epoch)) -} - -func keyClaim(claimID string) []byte { - return []byte(fmt.Sprintf("%s:%s", PrefixClaim, claimID)) -} - -func keyTaxProceeds(epoch sdk.Int) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixTaxProceeds, epoch)) -} - -func keyTaxCap(denom string) []byte { - return []byte(fmt.Sprintf("%s:%s", prefixTaxCap, denom)) -} - -func paramKeyTable() params.KeyTable { - return params.NewKeyTable( - paramStoreKeyParams, Params{}, - ) -} diff --git a/x/treasury/keeper_test.go b/x/treasury/keeper_test.go deleted file mode 100644 index 9d4f39d47..000000000 --- a/x/treasury/keeper_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package treasury - -import ( - "testing" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestRewardWeight(t *testing.T) { - input := createTestInput(t) - - // See that we can get and set reward weights - blocksPerEpoch := util.BlocksPerEpoch - for i := int64(0); i < 10; i++ { - input.ctx = input.ctx.WithBlockHeight(i * blocksPerEpoch) - - input.treasuryKeeper.SetRewardWeight(input.ctx, sdk.NewDecWithPrec(i, 2)) - } - - for i := int64(0); i < 10; i++ { - input.ctx = input.ctx.WithBlockHeight(i * blocksPerEpoch) - - require.Equal(t, sdk.NewDecWithPrec(i, 2), input.treasuryKeeper.GetRewardWeight(input.ctx, sdk.NewInt(i))) - } -} - -func TestTax(t *testing.T) { - input := createTestInput(t) - - // Set & get tax rate - testRate := sdk.NewDecWithPrec(2, 3) - input.treasuryKeeper.SetTaxRate(input.ctx, testRate) - curRate := input.treasuryKeeper.GetTaxRate(input.ctx, util.GetEpoch(input.ctx)) - require.Equal(t, curRate, testRate) - - // Vicariously set tax caps & test - params := DefaultParams() - input.treasuryKeeper.SetParams(input.ctx, params) - sdrCap := params.TaxPolicy.Cap - - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroSDRDenom, sdk.NewDec(1)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroCNYDenom, sdk.NewDec(10)) - input.oracleKeeper.SetLunaSwapRate(input.ctx, assets.MicroKRWDenom, sdk.NewDec(100)) - - readSdrCap := input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroSDRDenom) - cnyCap := input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroCNYDenom) - krwCap := input.treasuryKeeper.GetTaxCap(input.ctx, assets.MicroKRWDenom) - - require.Equal(t, sdrCap.Amount, readSdrCap) - require.Equal(t, sdrCap.Amount.MulRaw(10), cnyCap) - require.Equal(t, sdrCap.Amount.MulRaw(100), krwCap) -} - -func TestParams(t *testing.T) { - input := createTestInput(t) - - defaultParams := DefaultParams() - input.treasuryKeeper.SetParams(input.ctx, defaultParams) - - retrievedParams := input.treasuryKeeper.GetParams(input.ctx) - require.Equal(t, defaultParams, retrievedParams) -} diff --git a/x/treasury/module.go b/x/treasury/module.go new file mode 100644 index 000000000..3bcd1f650 --- /dev/null +++ b/x/treasury/module.go @@ -0,0 +1,126 @@ +package treasury + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/terra-project/core/x/treasury/client/cli" + "github.com/terra-project/core/x/treasury/client/rest" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +// module name +func (AppModuleBasic) Name() string { + return ModuleName +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(cdc) +} + +//___________________________ +// app module +type AppModule struct { + AppModuleBasic + + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + } +} + +// module name +func (AppModule) Name() string { return ModuleName } + +// register invariants +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// module message route name +func (AppModule) Route() string { return RouterKey } + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return nil +} + +// module querier route name +func (AppModule) QuerierRoute() string { return RouterKey } + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, genesisState) + return []abci.ValidatorUpdate{} +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + genesisState := ExportGenesis(ctx, am.keeper) + data := ModuleCdc.MustMarshalJSON(genesisState) + return data +} + +// module begin-block +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + EndBlocker(ctx, am.keeper) + return []abci.ValidatorUpdate{} +} diff --git a/x/treasury/params.go b/x/treasury/params.go deleted file mode 100644 index 0d8d3ff35..000000000 --- a/x/treasury/params.go +++ /dev/null @@ -1,106 +0,0 @@ -package treasury - -import ( - "fmt" - - "github.com/terra-project/core/types/assets" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Params treasury parameters -type Params struct { - TaxPolicy PolicyConstraints `json:"tax_policy"` - RewardPolicy PolicyConstraints `json:"reward_policy"` - - SeigniorageBurdenTarget sdk.Dec `json:"seigniorage_burden_target"` - MiningIncrement sdk.Dec `json:"mining_increment"` - - WindowShort sdk.Int `json:"window_short"` - WindowLong sdk.Int `json:"window_long"` - WindowProbation sdk.Int `json:"window_probation"` -} - -// NewParams creates a new param instance -func NewParams( - taxPolicy, rewardPolicy PolicyConstraints, - seigniorageBurden sdk.Dec, - miningIncrement sdk.Dec, - windowShort, windowLong, windowProbation sdk.Int, -) Params { - return Params{ - TaxPolicy: taxPolicy, - RewardPolicy: rewardPolicy, - SeigniorageBurdenTarget: seigniorageBurden, - MiningIncrement: miningIncrement, - WindowShort: windowShort, - WindowLong: windowLong, - WindowProbation: windowProbation, - } -} - -// DefaultParams creates default treasury module parameters -func DefaultParams() Params { - return NewParams( - - // Tax update policy - PolicyConstraints{ - RateMin: sdk.NewDecWithPrec(5, 4), // 0.05% - RateMax: sdk.NewDecWithPrec(1, 2), // 1% - Cap: sdk.NewCoin(assets.MicroSDRDenom, sdk.OneInt().MulRaw(assets.MicroUnit)), // 1 SDR Tax cap - ChangeRateMax: sdk.NewDecWithPrec(25, 5), // 0.025% - }, - - // Reward update policy - PolicyConstraints{ - RateMin: sdk.NewDecWithPrec(5, 2), // 5% - RateMax: sdk.NewDecWithPrec(90, 2), // 90% - ChangeRateMax: sdk.NewDecWithPrec(25, 3), // 2.5% - Cap: sdk.NewCoin("unused", sdk.ZeroInt()), // UNUSED - }, - - sdk.NewDecWithPrec(67, 2), // 67% - sdk.NewDecWithPrec(107, 2), // 1.07 mining increment; exponential growth - - sdk.NewInt(4), - sdk.NewInt(52), - sdk.NewInt(12), - ) -} - -func validateParams(params Params) error { - if params.TaxPolicy.RateMax.LT(params.TaxPolicy.RateMin) { - return fmt.Errorf("treasury TaxPolicy.RateMax %s must be greater than TaxPolicy.RateMin %s", - params.TaxPolicy.RateMax.String(), params.TaxPolicy.RateMin.String()) - } - - if params.TaxPolicy.RateMin.IsNegative() { - return fmt.Errorf("treasury parameter TaxPolicy.RateMin must be >= 0, is %s", params.TaxPolicy.RateMin.String()) - } - - if params.RewardPolicy.RateMax.LT(params.RewardPolicy.RateMin) { - return fmt.Errorf("treasury RewardPolicy.RateMax %s must be greater than RewardPolicy.RateMin %s", - params.RewardPolicy.RateMax.String(), params.RewardPolicy.RateMin.String()) - } - - if params.RewardPolicy.RateMin.IsNegative() { - return fmt.Errorf("treasury parameter RewardPolicy.RateMin must be >= 0, is %s", params.RewardPolicy.RateMin.String()) - } - - return nil -} - -// implements fmt.Stringer -func (params Params) String() string { - return fmt.Sprintf(`Treasury Params: - Tax Policy : { %v } - Reward Policy : { %v } - - SeigniorageBurdenTarget : %v - MiningIncrement : %v - - WindowShort : %v - WindowLong : %v - `, params.TaxPolicy, params.RewardPolicy, params.SeigniorageBurdenTarget, - params.MiningIncrement, params.WindowShort, params.WindowLong) -} diff --git a/x/treasury/querier.go b/x/treasury/querier.go deleted file mode 100644 index dad4a1770..000000000 --- a/x/treasury/querier.go +++ /dev/null @@ -1,233 +0,0 @@ -package treasury - -import ( - "strconv" - "strings" - - "github.com/terra-project/core/types/util" - - "github.com/cosmos/cosmos-sdk/codec" - - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" -) - -// query endpoints supported by the treasury Querier -const ( - QueryTaxRate = "tax-rate" - QueryTaxCap = "tax-cap" - QueryMiningRewardWeight = "reward-weight" - QuerySeigniorageProceeds = "seigniorage-proceeds" - QueryCurrentEpoch = "current-epoch" - QueryParams = "params" - QueryIssuance = "issuance" - QueryTaxProceeds = "tax-proceeds" -) - -// NewQuerier is the module level router for state queries -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case QueryTaxRate: - return queryTaxRate(ctx, path[1:], req, keeper) - case QueryTaxCap: - return queryTaxCap(ctx, path[1:], req, keeper) - case QueryMiningRewardWeight: - return queryMiningRewardWeight(ctx, path[1:], req, keeper) - case QueryTaxProceeds: - return queryTaxProceeds(ctx, path[1:], req, keeper) - case QuerySeigniorageProceeds: - return querySeigniorageProceeds(ctx, path[1:], req, keeper) - case QueryIssuance: - return queryIssuance(ctx, path[1:], req, keeper) - case QueryCurrentEpoch: - return queryCurrentEpoch(ctx, req, keeper) - case QueryParams: - return queryParams(ctx, req, keeper) - default: - return nil, sdk.ErrUnknownRequest("unknown treasury query endpoint") - } - } -} - -// JSON response format -type QueryTaxRateResponse struct { - TaxRate sdk.Dec `json:"tax_rate"` -} - -func (r QueryTaxRateResponse) String() (out string) { - out = r.TaxRate.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryTaxRate(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - epoch, ok := sdk.NewIntFromString(path[0]) - if !ok { - return nil, sdk.ErrInternal("epoch parameter is not correctly formatted") - } - - taxRate := keeper.GetTaxRate(ctx, epoch) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryTaxRateResponse{TaxRate: taxRate}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QueryTaxCapResponse struct { - TaxCap sdk.Int `json:"tax_cap"` -} - -func (r QueryTaxCapResponse) String() (out string) { - out = r.TaxCap.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryTaxCap(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - denom := path[0] - taxCap := keeper.GetTaxCap(ctx, denom) - - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryTaxCapResponse{TaxCap: taxCap}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QueryIssuanceResponse struct { - Issuance sdk.Int `json:"issuance"` -} - -func (r QueryIssuanceResponse) String() (out string) { - out = r.Issuance.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryIssuance(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - denom := path[0] - var dayStr string - if len(path) == 2 { - dayStr = path[1] - } - - var day int64 - if len(dayStr) == 0 { - day = ctx.BlockHeight() / util.BlocksPerDay - } else { - day, _ = strconv.ParseInt(dayStr, 10, 64) - } - - issuance := keeper.mtk.GetIssuance(ctx, denom, sdk.NewInt(day)) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryIssuanceResponse{Issuance: issuance}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QueryMiningRewardWeightResponse struct { - RewardWeight sdk.Dec `json:"reward_weight"` -} - -func (r QueryMiningRewardWeightResponse) String() (out string) { - out = r.RewardWeight.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryMiningRewardWeight(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - epoch, ok := sdk.NewIntFromString(path[0]) - if !ok { - return nil, sdk.ErrInternal("epoch parameter is not correctly formatted") - } - - rewardWeight := keeper.GetRewardWeight(ctx, epoch) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryMiningRewardWeightResponse{RewardWeight: rewardWeight}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QueryTaxProceedsResponse struct { - TaxProceeds sdk.Coins `json:"tax_proceeds"` -} - -func (r QueryTaxProceedsResponse) String() (out string) { - out = r.TaxProceeds.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func queryTaxProceeds(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - epoch, ok := sdk.NewIntFromString(path[0]) - if !ok { - return nil, sdk.ErrInternal("epoch parameter is not correctly formatted") - } - - pool := keeper.PeekTaxProceeds(ctx, epoch) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryTaxProceedsResponse{TaxProceeds: pool}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QuerySeigniorageProceedsResponse struct { - SeigniorageProceeds sdk.Int `json:"seigniorage_proceeds"` -} - -func (r QuerySeigniorageProceedsResponse) String() (out string) { - out = r.SeigniorageProceeds.String() - return strings.TrimSpace(out) -} - -// nolint: unparam -func querySeigniorageProceeds(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - epoch, ok := sdk.NewIntFromString(path[0]) - if !ok { - return nil, sdk.ErrInternal("epoch parameter is not correctly formatted") - } - - pool := keeper.mtk.PeekEpochSeigniorage(ctx, epoch) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QuerySeigniorageProceedsResponse{SeigniorageProceeds: pool}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -// JSON response format -type QueryCurrentEpochResponse struct { - CurrentEpoch sdk.Int `json:"current_epoch"` -} - -func (r QueryCurrentEpochResponse) String() (out string) { - out = r.CurrentEpoch.String() - return strings.TrimSpace(out) -} - -func queryCurrentEpoch(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - curEpoch := util.GetEpoch(ctx) - bz, err := codec.MarshalJSONIndent(keeper.cdc, QueryCurrentEpochResponse{CurrentEpoch: curEpoch}) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} - -func queryParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetParams(ctx)) - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil -} diff --git a/x/treasury/querier_test.go b/x/treasury/querier_test.go deleted file mode 100644 index 7aba6f476..000000000 --- a/x/treasury/querier_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package treasury - -import ( - "strings" - "testing" - - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/types/util" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const custom = "custom" - -func getQueriedTaxRate(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch sdk.Int) sdk.Dec { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryTaxRate}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryTaxRate, epoch.String()}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var reseponse QueryTaxRateResponse - err2 := cdc.UnmarshalJSON(bz, &reseponse) - require.Nil(t, err2) - - return reseponse.TaxRate -} - -func getQueriedTaxCap(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, denom string) sdk.Int { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryTaxCap}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryTaxCap, denom}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryTaxCapResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.TaxCap -} - -func getQueriedRewardWeight(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch sdk.Int) sdk.Dec { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryMiningRewardWeight}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryMiningRewardWeight, epoch.String()}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryMiningRewardWeightResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.RewardWeight -} - -func getQueriedTaxProceeds(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch sdk.Int) sdk.Coins { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryTaxProceeds}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryTaxProceeds, epoch.String()}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryTaxProceedsResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.TaxProceeds -} - -func getQueriedSeigniorageProceeds(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, epoch sdk.Int) sdk.Coin { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QuerySeigniorageProceeds}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QuerySeigniorageProceeds, epoch.String()}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QuerySeigniorageProceedsResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return sdk.NewCoin(assets.MicroLunaDenom, response.SeigniorageProceeds) -} - -func getQueriedIssuance(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, denom string) sdk.Int { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryIssuance}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryIssuance, denom}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - bz, err = querier(ctx, []string{QueryIssuance, denom, "0"}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryIssuanceResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.Issuance -} - -func getQueriedCurrentEpoch(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) sdk.Int { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryCurrentEpoch}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryCurrentEpoch}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var response QueryCurrentEpochResponse - err2 := cdc.UnmarshalJSON(bz, &response) - require.Nil(t, err2) - - return response.CurrentEpoch -} - -func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) Params { - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, QuerierRoute, QueryParams}, "/"), - Data: []byte{}, - } - - bz, err := querier(ctx, []string{QueryParams}, query) - require.Nil(t, err) - require.NotNil(t, bz) - - var params Params - err2 := cdc.UnmarshalJSON(bz, ¶ms) - require.Nil(t, err2) - - return params -} - -func TestQueryParams(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - params := DefaultParams() - input.treasuryKeeper.SetParams(input.ctx, params) - - queriedParams := getQueriedParams(t, input.ctx, input.cdc, querier) - - require.Equal(t, queriedParams, params) -} - -func TestQueryRewardWeight(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - rewardWeight := sdk.NewDecWithPrec(77, 2) - input.treasuryKeeper.SetRewardWeight(input.ctx, rewardWeight) - - queriedRewardWeight := getQueriedRewardWeight(t, input.ctx, input.cdc, querier, util.GetEpoch(input.ctx)) - - require.Equal(t, queriedRewardWeight, rewardWeight) -} - -func TestQueryTaxRate(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - taxRate := sdk.NewDecWithPrec(1, 3) - input.treasuryKeeper.SetTaxRate(input.ctx, taxRate) - - queriedTaxRate := getQueriedTaxRate(t, input.ctx, input.cdc, querier, util.GetEpoch(input.ctx)) - - require.Equal(t, queriedTaxRate, taxRate) -} - -func TestQueryTaxCap(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - params := input.treasuryKeeper.GetParams(input.ctx) - - // Get a currency super random; should default to policy coin. - queriedTaxCap := getQueriedTaxCap(t, input.ctx, input.cdc, querier, "hello") - - require.Equal(t, queriedTaxCap, params.TaxPolicy.Cap.Amount) -} - -func TestQueryCurrentEpoch(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - curEpoch := util.GetEpoch(input.ctx) - - queriedCurEpoch := getQueriedCurrentEpoch(t, input.ctx, input.cdc, querier) - - require.Equal(t, queriedCurEpoch, curEpoch) -} - -func TestQueryTaxProceeds(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - taxProceeds := sdk.Coins{ - sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1000).MulRaw(assets.MicroUnit)), - } - input.treasuryKeeper.RecordTaxProceeds(input.ctx, taxProceeds) - - queriedTaxProceeds := getQueriedTaxProceeds(t, input.ctx, input.cdc, querier, util.GetEpoch(input.ctx)) - - require.Equal(t, queriedTaxProceeds, taxProceeds) -} - -func TestQuerySeigniorageProceeds(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - seigniorageProceeds := sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(10).MulRaw(assets.MicroUnit)) - input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, seigniorageProceeds.Amount)) - - getQueriedSeigniorageProceeds(t, input.ctx, input.cdc, querier, util.GetEpoch(input.ctx)) - - input.ctx = input.ctx.WithBlockHeight(util.BlocksPerEpoch) - input.mintKeeper.Burn(input.ctx, addrs[0], sdk.NewCoin(assets.MicroLunaDenom, seigniorageProceeds.Amount)) - - queriedSeigniorageProceeds := getQueriedSeigniorageProceeds(t, input.ctx, input.cdc, querier, util.GetEpoch(input.ctx)) - - require.Equal(t, seigniorageProceeds, queriedSeigniorageProceeds) -} - -func TestQueryIssuance(t *testing.T) { - input := createTestInput(t) - querier := NewQuerier(input.treasuryKeeper) - - issuance := sdk.NewInt(1000).MulRaw(assets.MicroUnit) - err := input.mintKeeper.Mint(input.ctx, addrs[0], sdk.NewCoin(assets.MicroSDRDenom, issuance)) - require.Nil(t, err) - - queriedIssuance := getQueriedIssuance(t, input.ctx, input.cdc, querier, assets.MicroSDRDenom) - - require.Equal(t, issuance, queriedIssuance) -} diff --git a/x/treasury/tags/tags.go b/x/treasury/tags/tags.go deleted file mode 100644 index 1e411f7fd..000000000 --- a/x/treasury/tags/tags.go +++ /dev/null @@ -1,21 +0,0 @@ -package tags - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Treasury tags -var ( - ActionSettle = "settle" - ActionPolicyUpdate = "policy-update" - - Action = sdk.TagAction - Denom = "denom" - Amount = "amount" - Rewardee = "rewardee" - Tax = "tax" - Class = "class" - MinerReward = "miner-weight" - Oracle = "oracle-reward" - Budget = "budget" -) diff --git a/x/treasury/test_common.go b/x/treasury/test_common.go deleted file mode 100644 index 9b622611c..000000000 --- a/x/treasury/test_common.go +++ /dev/null @@ -1,205 +0,0 @@ -package treasury - -import ( - "github.com/terra-project/core/types/assets" - "github.com/terra-project/core/x/market" - "github.com/terra-project/core/x/mint" - "github.com/terra-project/core/x/oracle" - - "testing" - "time" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -var ( - pubKeys = []crypto.PubKey{ - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - secp256k1.GenPrivKey().PubKey(), - } - - addrs = []sdk.AccAddress{ - sdk.AccAddress(pubKeys[0].Address()), - sdk.AccAddress(pubKeys[1].Address()), - sdk.AccAddress(pubKeys[2].Address()), - } - - valConsPubKeys = []crypto.PubKey{ - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - } - - valConsAddrs = []sdk.ConsAddress{ - sdk.ConsAddress(valConsPubKeys[0].Address()), - sdk.ConsAddress(valConsPubKeys[1].Address()), - sdk.ConsAddress(valConsPubKeys[2].Address()), - } - - uLunaAmt = sdk.NewInt(1000).MulRaw(assets.MicroUnit) -) - -type testInput struct { - ctx sdk.Context - cdc *codec.Codec - bankKeeper bank.Keeper - oracleKeeper oracle.Keeper - marketKeeper market.Keeper - mintKeeper mint.Keeper - treasuryKeeper Keeper - distrKeeper distr.Keeper -} - -func newTestCodec() *codec.Codec { - cdc := codec.New() - - RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T) testInput { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keyMint := sdk.NewKVStoreKey(mint.StoreKey) - keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) - keyTreasury := sdk.NewKVStoreKey(StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) - keyDistr := sdk.NewKVStoreKey(distr.StoreKey) - tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) - keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) - keyMarket := sdk.NewKVStoreKey(market.StoreKey) - - cdc := newTestCodec() - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyTreasury, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) - - require.NoError(t, ms.LoadLatestVersion()) - - paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) - accKeeper := auth.NewAccountKeeper( - cdc, - keyAcc, - paramsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - bankKeeper := bank.NewBaseKeeper( - accKeeper, - paramsKeeper.Subspace(bank.DefaultParamspace), - bank.DefaultCodespace, - ) - - stakingKeeper := staking.NewKeeper( - cdc, - keyStaking, tKeyStaking, - bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), - staking.DefaultCodespace, - ) - - feeCollectionKeeper := auth.NewFeeCollectionKeeper( - cdc, - keyFeeCollection, - ) - - distrKeeper := distr.NewKeeper( - cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), - bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, - ) - - stakingKeeper.SetPool(ctx, staking.InitialPool()) - stakingParams := staking.DefaultParams() - stakingParams.BondDenom = assets.MicroLunaDenom - stakingKeeper.SetParams(ctx, stakingParams) - - mintKeeper := mint.NewKeeper( - cdc, - keyMint, - stakingKeeper, - bankKeeper, - accKeeper, - ) - - sh := staking.NewHandler(stakingKeeper) - for i, addr := range addrs { - err2 := mintKeeper.Mint(ctx, addr, sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt)) - require.NoError(t, err2) - - // Add validators - commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(sdk.ValAddress(addr), valConsPubKeys[i], - sdk.NewCoin(assets.MicroLunaDenom, uLunaAmt), staking.Description{}, commission, sdk.OneInt()) - res := sh(ctx, msg) - require.True(t, res.IsOK()) - - distrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(addr)) - staking.EndBlocker(ctx, stakingKeeper) - } - - oracleKeeper := oracle.NewKeeper( - cdc, - keyOracle, - mintKeeper, - distrKeeper, - feeCollectionKeeper, - stakingKeeper.GetValidatorSet(), - paramsKeeper.Subspace(oracle.DefaultParamspace), - ) - - marketKeeper := market.NewKeeper( - cdc, - keyMarket, - oracleKeeper, - mintKeeper, - paramsKeeper.Subspace(market.DefaultParamspace)) - - marketKeeper.SetParams(ctx, market.DefaultParams()) - - treasuryKeeper := NewKeeper( - cdc, - keyTreasury, - stakingKeeper.GetValidatorSet(), - mintKeeper, - marketKeeper, - paramsKeeper.Subspace(DefaultParamspace), - ) - - InitGenesis(ctx, treasuryKeeper, DefaultGenesisState()) - - return testInput{ctx, cdc, bankKeeper, oracleKeeper, marketKeeper, mintKeeper, treasuryKeeper, distrKeeper} -}