From f9c39740cda0f02adf5ce40ab7e02945abe1358f Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Jul 2018 14:18:03 -0400 Subject: [PATCH 01/24] remove old abci scripts --- abci/scripts/abci-builder/Dockerfile | 12 ------- abci/scripts/dist.sh | 52 --------------------------- abci/scripts/dist_build.sh | 53 ---------------------------- abci/scripts/publish.sh | 7 ---- scripts/dep_utils/parse.sh | 14 -------- scripts/install_abci_apps.sh | 12 ------- 6 files changed, 150 deletions(-) delete mode 100644 abci/scripts/abci-builder/Dockerfile delete mode 100755 abci/scripts/dist.sh delete mode 100755 abci/scripts/dist_build.sh delete mode 100644 abci/scripts/publish.sh delete mode 100644 scripts/dep_utils/parse.sh delete mode 100644 scripts/install_abci_apps.sh diff --git a/abci/scripts/abci-builder/Dockerfile b/abci/scripts/abci-builder/Dockerfile deleted file mode 100644 index 1182085b4..000000000 --- a/abci/scripts/abci-builder/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.9.2 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - zip \ - && rm -rf /var/lib/apt/lists/* - -# We want to ensure that release builds never have any cgo dependencies so we -# switch that off at the highest level. -ENV CGO_ENABLED 0 - -RUN mkdir -p $GOPATH/src/github.com/tendermint/abci -WORKDIR $GOPATH/src/github.com/tendermint/abci diff --git a/abci/scripts/dist.sh b/abci/scripts/dist.sh deleted file mode 100755 index d94ce20f7..000000000 --- a/abci/scripts/dist.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -e - -REPO_NAME="abci" - -# Get the version from the environment, or try to figure it out. -if [ -z $VERSION ]; then - VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) -fi -if [ -z "$VERSION" ]; then - echo "Please specify a version." - exit 1 -fi -echo "==> Building version $VERSION..." - -# Get the parent directory of where this script is. -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done -DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" - -# Change into that dir because we expect that. -cd "$DIR" - -# Delete the old dir -echo "==> Removing old directory..." -rm -rf build/pkg -mkdir -p build/pkg - - -# Do a hermetic build inside a Docker container. -docker build -t tendermint/${REPO_NAME}-builder scripts/${REPO_NAME}-builder/ -docker run --rm -e "BUILD_TAGS=$BUILD_TAGS" -v "$(pwd)":/go/src/github.com/tendermint/${REPO_NAME} tendermint/${REPO_NAME}-builder ./scripts/dist_build.sh - -# Add $REPO_NAME and $VERSION prefix to package name. -rm -rf ./build/dist -mkdir -p ./build/dist -for FILENAME in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type f); do - FILENAME=$(basename "$FILENAME") - cp "./build/pkg/${FILENAME}" "./build/dist/${REPO_NAME}_${VERSION}_${FILENAME}" -done - -# Make the checksums. -pushd ./build/dist -shasum -a256 ./* > "./${REPO_NAME}_${VERSION}_SHA256SUMS" -popd - -# Done -echo -echo "==> Results:" -ls -hl ./build/dist - -exit 0 diff --git a/abci/scripts/dist_build.sh b/abci/scripts/dist_build.sh deleted file mode 100755 index c45c752ef..000000000 --- a/abci/scripts/dist_build.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Get the parent directory of where this script is. -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done -DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" - -# Change into that dir because we expect that. -cd "$DIR" - -# Get the git commit -GIT_COMMIT="$(git rev-parse --short HEAD)" -GIT_DESCRIBE="$(git describe --tags --always)" -GIT_IMPORT="github.com/tendermint/abci/version" - -# Determine the arch/os combos we're building for -XC_ARCH=${XC_ARCH:-"386 amd64 arm"} -XC_OS=${XC_OS:-"solaris darwin freebsd linux windows"} - -# Make sure build tools are available. -make get_tools - -# Get VENDORED dependencies -make get_vendor_deps - -BINARY="abci-cli" - -# Build! -echo "==> Building..." -"$(which gox)" \ - -os="${XC_OS}" \ - -arch="${XC_ARCH}" \ - -osarch="!darwin/arm !solaris/amd64 !freebsd/amd64" \ - -ldflags "-X ${GIT_IMPORT}.GitCommit='${GIT_COMMIT}' -X ${GIT_IMPORT}.GitDescribe='${GIT_DESCRIBE}'" \ - -output "build/pkg/{{.OS}}_{{.Arch}}/$BINARY" \ - -tags="${BUILD_TAGS}" \ - github.com/tendermint/abci/cmd/$BINARY - -# Zip all the files. -echo "==> Packaging..." -for PLATFORM in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type d); do - OSARCH=$(basename "${PLATFORM}") - echo "--> ${OSARCH}" - - pushd "$PLATFORM" >/dev/null 2>&1 - zip "../${OSARCH}.zip" ./* - popd >/dev/null 2>&1 -done - - - -exit 0 diff --git a/abci/scripts/publish.sh b/abci/scripts/publish.sh deleted file mode 100644 index 715f6c11b..000000000 --- a/abci/scripts/publish.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash - -# Get the version from the environment, or try to figure it out. -if [ -z $VERSION ]; then - VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) -fi -aws s3 cp --recursive build/dist s3://tendermint/binaries/abci/v${VERSION} --acl public-read diff --git a/scripts/dep_utils/parse.sh b/scripts/dep_utils/parse.sh deleted file mode 100644 index e6519efa0..000000000 --- a/scripts/dep_utils/parse.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -set +u -if [[ "$DEP" == "" ]]; then - DEP=$GOPATH/src/github.com/tendermint/tendermint/Gopkg.lock -fi -set -u - - -set -euo pipefail - -LIB=$1 - -grep -A100 "$LIB" "$DEP" | grep revision | head -n1 | grep -o '"[^"]\+"' | cut -d '"' -f 2 diff --git a/scripts/install_abci_apps.sh b/scripts/install_abci_apps.sh deleted file mode 100644 index eb70070df..000000000 --- a/scripts/install_abci_apps.sh +++ /dev/null @@ -1,12 +0,0 @@ -#! /bin/bash - -# get the abci commit used by tendermint -COMMIT=$(bash scripts/dep_utils/parse.sh abci) -echo "Checking out vendored commit for abci: $COMMIT" - -go get -d github.com/tendermint/abci -cd "$GOPATH/src/github.com/tendermint/abci" || exit -git checkout "$COMMIT" -make get_tools -make get_vendor_deps -make install From cf9d63628b66159f9001484122c6b747c428c97a Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Jul 2018 14:19:33 -0400 Subject: [PATCH 02/24] move abci Dockerfile (still needs to be updated tho) --- abci/Dockerfile.develop => DOCKER/Dockerfile.abci | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename abci/Dockerfile.develop => DOCKER/Dockerfile.abci (100%) diff --git a/abci/Dockerfile.develop b/DOCKER/Dockerfile.abci similarity index 100% rename from abci/Dockerfile.develop rename to DOCKER/Dockerfile.abci From 0c393b5c620fc925b270d99a31fca6606f6f2af4 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Jul 2018 14:48:45 -0400 Subject: [PATCH 03/24] abci mostly done, still gonna want to consolidate the tests --- Makefile | 52 ++++++++++-- abci/Makefile | 174 --------------------------------------- abci/tests/test_cover.sh | 13 --- 3 files changed, 46 insertions(+), 193 deletions(-) delete mode 100644 abci/Makefile delete mode 100755 abci/tests/test_cover.sh diff --git a/Makefile b/Makefile index 079c58f90..5c8df95ea 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,11 @@ GOTOOLS = \ + github.com/mitchellh/gox \ github.com/golang/dep/cmd/dep \ - gopkg.in/alecthomas/gometalinter.v2 + gopkg.in/alecthomas/gometalinter.v2 \ + github.com/gogo/protobuf/protoc-gen-gogo \ + github.com/gogo/protobuf/gogoproto PACKAGES=$(shell go list ./... | grep -v '/vendor/') +INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?=tendermint BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" @@ -11,7 +15,7 @@ check: check_tools ensure_deps ######################################## -### Build +### Build Tendermint build: CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/ @@ -22,10 +26,29 @@ build_race: install: CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' ./cmd/tendermint +######################################## +### Build ABCI + +protoc: + ## If you get the following error, + ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" + ## See https://stackoverflow.com/a/25518702 + protoc $(INCLUDE) --gogo_out=plugins=grpc:. abci/types/*.proto + @echo "--> adding nolint declarations to protobuf generated files" + @awk '/package abci/types/ { print "//nolint: gas"; print; next }1' abci/types/types.pb.go > abci/types/types.pb.go.new + @mv abci/types/types.pb.go.new abci/types/types.pb.go + +build_abci: + @go build -i ./abci/cmd/... + +install_abci: + @go install ./abci/cmd/... + ######################################## ### Distribution # dist builds binaries for all platforms and packages them for distribution +# TODO add abci to these scripts dist: @BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/dist.sh'" @@ -59,6 +82,17 @@ ensure_deps: @echo "--> Running dep" @dep ensure +#For ABCI +get_protoc: + @# https://github.com/google/protobuf/releases + curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \ + cd protobuf-3.4.1 && \ + DIST_LANG=cpp ./configure && \ + make && \ + make install && \ + cd .. && \ + rm -rf protobuf-3.4.1 + draw_deps: @# requires brew install graphviz or apt-get install graphviz go get github.com/RobotsAndPencils/goviz @@ -87,6 +121,15 @@ test_apps: # requires `abci-cli` and `tendermint` binaries installed bash test/app/test.sh +test_abci_apps: + bash abci/tests/test_app/test.sh + +test_abci_cli: + # test the cli against the examples in the tutorial at: + # ./docs/abci-cli.md + # if test fails, update the docs ^ + @ bash abci/tests/test_cli/test.sh + test_persistence: # run the persistence tests using bash # requires `abci-cli` installed @@ -105,17 +148,14 @@ test_p2p: # requires 'tester' the image from above bash test/p2p/test.sh tester -need_abci: - bash scripts/install_abci_apps.sh - test_integrations: make build_docker_test_image make get_tools make get_vendor_deps make install - make need_abci make test_cover make test_apps + make test_abci_cli make test_persistence make test_p2p diff --git a/abci/Makefile b/abci/Makefile deleted file mode 100644 index 7d1c4b2e5..000000000 --- a/abci/Makefile +++ /dev/null @@ -1,174 +0,0 @@ -GOTOOLS = \ - github.com/mitchellh/gox \ - github.com/golang/dep/cmd/dep \ - gopkg.in/alecthomas/gometalinter.v2 \ - github.com/gogo/protobuf/protoc-gen-gogo \ - github.com/gogo/protobuf/gogoproto -GOTOOLS_CHECK = gox dep gometalinter.v2 protoc protoc-gen-gogo -PACKAGES=$(shell go list ./... | grep -v '/vendor/') -INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf - -all: check get_vendor_deps protoc build test install metalinter - -check: check_tools - - -######################################## -### Build - -protoc: - ## If you get the following error, - ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" - ## See https://stackoverflow.com/a/25518702 - protoc $(INCLUDE) --gogo_out=plugins=grpc:. types/*.proto - @echo "--> adding nolint declarations to protobuf generated files" - @awk '/package types/ { print "//nolint: gas"; print; next }1' types/types.pb.go > types/types.pb.go.new - @mv types/types.pb.go.new types/types.pb.go - -build: - @go build -i ./cmd/... - -dist: - @bash scripts/dist.sh - @bash scripts/publish.sh - -install: - @go install ./cmd/... - - -######################################## -### Tools & dependencies - -check_tools: - @# https://stackoverflow.com/a/25668869 - @echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ - $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" - -get_tools: - @echo "--> Installing tools" - go get -u -v $(GOTOOLS) - @gometalinter.v2 --install - -get_protoc: - @# https://github.com/google/protobuf/releases - curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \ - cd protobuf-3.4.1 && \ - DIST_LANG=cpp ./configure && \ - make && \ - make install && \ - cd .. && \ - rm -rf protobuf-3.4.1 - -update_tools: - @echo "--> Updating tools" - @go get -u $(GOTOOLS) - -get_vendor_deps: - @rm -rf vendor/ - @echo "--> Running dep ensure" - @dep ensure - - -######################################## -### Testing - -test: - @find . -path ./vendor -prune -o -name "*.sock" -exec rm {} \; - @echo "==> Running go test" - @go test $(PACKAGES) - -test_race: - @find . -path ./vendor -prune -o -name "*.sock" -exec rm {} \; - @echo "==> Running go test --race" - @go test -v -race $(PACKAGES) - -### three tests tested by Jenkins -test_cover: - @ bash tests/test_cover.sh - -test_apps: - # test the counter using a go test script - @ bash tests/test_app/test.sh - -test_cli: - # test the cli against the examples in the tutorial at: - # http://tendermint.readthedocs.io/projects/tools/en/master/abci-cli.html - # - # XXX: if this test fails, fix it and update the docs at: - # https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.rst - @ bash tests/test_cli/test.sh - -######################################## -### Formatting, linting, and vetting - -fmt: - @go fmt ./... - -metalinter: - @echo "==> Running linter" - gometalinter.v2 --vendor --deadline=600s --disable-all \ - --enable=maligned \ - --enable=deadcode \ - --enable=goconst \ - --enable=goimports \ - --enable=gosimple \ - --enable=ineffassign \ - --enable=megacheck \ - --enable=misspell \ - --enable=staticcheck \ - --enable=safesql \ - --enable=structcheck \ - --enable=unconvert \ - --enable=unused \ - --enable=varcheck \ - --enable=vetshadow \ - ./... - #--enable=gas \ - #--enable=dupl \ - #--enable=errcheck \ - #--enable=gocyclo \ - #--enable=golint \ <== comments on anything exported - #--enable=gotype \ - #--enable=interfacer \ - #--enable=unparam \ - #--enable=vet \ - -metalinter_all: - protoc $(INCLUDE) --lint_out=. types/*.proto - gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... - - -######################################## -### Docker - -DEVDOC_SAVE = docker commit `docker ps -a -n 1 -q` devdoc:local - -docker_build: - docker build -t "tendermint/abci-dev" -f Dockerfile.develop . - -docker_run: - docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" "tendermint/abci-dev" /bin/bash - -docker_run_rm: - docker run -it --rm -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" "tendermint/abci-dev" /bin/bash - -devdoc_init: - docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" tendermint/devdoc echo - # TODO make this safer - $(call DEVDOC_SAVE) - -devdoc: - docker run -it -v "$(CURDIR):/go/src/github.com/tendermint/abci" -w "/go/src/github.com/tendermint/abci" devdoc:local bash - -devdoc_save: - # TODO make this safer - $(call DEVDOC_SAVE) - -devdoc_clean: - docker rmi $$(docker images -f "dangling=true" -q) - - -# 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: check protoc build dist install check_tools get_tools get_protoc update_tools get_vendor_deps test test_race fmt metalinter metalinter_all docker_build docker_run docker_run_rm devdoc_init devdoc devdoc_save devdoc_clean diff --git a/abci/tests/test_cover.sh b/abci/tests/test_cover.sh deleted file mode 100755 index abbbbe563..000000000 --- a/abci/tests/test_cover.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -echo "==> Running unit tests" -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done From bc010ab5fa6b3c72f0cd83d7d5fb823d0a211ea1 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Jul 2018 14:54:53 -0400 Subject: [PATCH 04/24] updates --- .circleci/config.yml | 3 +-- Makefile | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a50a61c5..aa6a1089f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,8 +31,7 @@ jobs: name: binaries command: | export PATH="$GOBIN:$PATH" - make install - cd abci && make install + make install install_abci - persist_to_workspace: root: /tmp/workspace paths: diff --git a/Makefile b/Makefile index 5c8df95ea..0af81612a 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,7 @@ test_integrations: make install make test_cover make test_apps + make test_abci_apps make test_abci_cli make test_persistence make test_p2p From f7156afee3a38273eb757e868dc264a5c26eae0c Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Jul 2018 15:09:06 -0400 Subject: [PATCH 05/24] repo bloat artifacts errrrywhere --- test/docker/Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index bc211ea43..8b69d27fe 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -21,15 +21,12 @@ ADD Makefile Makefile RUN make get_tools RUN make get_vendor_deps -# Install the apps -ADD scripts scripts -RUN bash scripts/install_abci_apps.sh - # Now copy in the code # NOTE: this will overwrite whatever is in vendor/ COPY . $REPO RUN go install ./cmd/tendermint +RUN go install ./abci/cmd/abci-cli # expose the volume for debugging VOLUME $REPO From 030c782e6fa2ae193c6a926c94366ea3480424fa Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 4 Jul 2018 17:11:34 -0400 Subject: [PATCH 06/24] Zach/1793 repo consolidation v2 (#1907) --- Makefile | 41 +++++++++++++-- crypto/Makefile | 99 ---------------------------------- libs/Makefile | 137 ------------------------------------------------ 3 files changed, 37 insertions(+), 240 deletions(-) delete mode 100644 crypto/Makefile delete mode 100644 libs/Makefile diff --git a/Makefile b/Makefile index 0af81612a..b929dbe5c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,8 @@ GOTOOLS = \ github.com/golang/dep/cmd/dep \ gopkg.in/alecthomas/gometalinter.v2 \ github.com/gogo/protobuf/protoc-gen-gogo \ - github.com/gogo/protobuf/gogoproto + github.com/gogo/protobuf/gogoproto \ + github.com/square/certstrap PACKAGES=$(shell go list ./... | grep -v '/vendor/') INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?=tendermint @@ -29,7 +30,7 @@ install: ######################################## ### Build ABCI -protoc: +protoc_abci: ## If you get the following error, ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" ## See https://stackoverflow.com/a/25518702 @@ -82,7 +83,7 @@ ensure_deps: @echo "--> Running dep" @dep ensure -#For ABCI +#For ABCI and libs get_protoc: @# https://github.com/google/protobuf/releases curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \ @@ -104,6 +105,37 @@ get_deps_bin_size: @find $(WORK) -type f -name "*.a" | xargs -I{} du -hxs "{}" | sort -rh | sed -e s:${WORK}/::g > deps_bin_size.log @echo "Results can be found here: $(CURDIR)/deps_bin_size.log" +######################################## +### Libs + +protoc_libs: + ## If you get the following error, + ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" + ## See https://stackoverflow.com/a/25518702 + protoc $(INCLUDE) --go_out=plugins=grpc:. libs/common/*.proto + @echo "--> adding nolint declarations to protobuf generated files" + @awk '/package libs/common/ { print "//nolint: gas"; print; next }1' libs/common/types.pb.go > libs/common/types.pb.go.new + @mv libs/common/types.pb.go.new libs/common/types.pb.go + +gen_certs: clean_certs + ## Generating certificates for TLS testing... + certstrap init --common-name "tendermint.com" --passphrase "" + certstrap request-cert -ip "::" --passphrase "" + certstrap sign "::" --CA "tendermint.com" --passphrase "" + mv out/::.crt out/::.key db/remotedb + +clean_certs: + ## Cleaning TLS testing certificates... + rm -rf out + rm -f db/remotedb/::.crt db/remotedb/::.key + +test_libs: gen_certs + GOCACHE=off go test -tags gcc $(shell go list ./... | grep -v vendor) + make clean_certs + +grpc_dbserver: + protoc -I db/remotedb/proto/ db/remotedb/proto/defs.proto --go_out=plugins=grpc:db/remotedb/proto + ######################################## ### Testing @@ -157,6 +189,7 @@ test_integrations: make test_apps make test_abci_apps make test_abci_cli + make test_libs make test_persistence make test_p2p @@ -274,4 +307,4 @@ build-slate: # 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: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate +.PHONY: check build build_race build_abci dist install install_abci check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate diff --git a/crypto/Makefile b/crypto/Makefile deleted file mode 100644 index a4fd3c37f..000000000 --- a/crypto/Makefile +++ /dev/null @@ -1,99 +0,0 @@ -GOTOOLS = \ - github.com/golang/dep/cmd/dep \ - # gopkg.in/alecthomas/gometalinter.v2 \ - -GOTOOLS_CHECK = dep #gometalinter.v2 - -all: check get_vendor_deps build test install - -check: check_tools - - -######################################## -### Build - -# Command to generate the workd list (kept here for documentation purposes only): -wordlist: - # To re-generate wordlist.go run: - # go-bindata -ignore ".*\.go" -o keys/words/bip39/wordlist.go -pkg "wordlist" keys/bip39/wordlist/... - -build: wordlist - # Nothing else to build! - -install: - # Nothing to install! - - -######################################## -### Tools & dependencies - -check_tools: - @# https://stackoverflow.com/a/25668869 - @echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ - $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" - -get_tools: - @echo "--> Installing tools" - go get -u -v $(GOTOOLS) - #@gometalinter.v2 --install - -update_tools: - @echo "--> Updating tools" - @go get -u $(GOTOOLS) - -get_vendor_deps: - @rm -rf vendor/ - @echo "--> Running dep ensure" - @dep ensure - - -######################################## -### Testing - -test: - CGO_ENABLED=0 go test -p 1 $(shell go list ./... | grep -v vendor) - -######################################## -### Formatting, linting, and vetting - -fmt: - @go fmt ./... - -metalinter: - @echo "==> Running linter" - gometalinter.v2 --vendor --deadline=600s --disable-all \ - --enable=maligned \ - --enable=deadcode \ - --enable=goconst \ - --enable=goimports \ - --enable=gosimple \ - --enable=ineffassign \ - --enable=megacheck \ - --enable=misspell \ - --enable=staticcheck \ - --enable=safesql \ - --enable=structcheck \ - --enable=unconvert \ - --enable=unused \ - --enable=varcheck \ - --enable=vetshadow \ - ./... - #--enable=gas \ - #--enable=dupl \ - #--enable=errcheck \ - #--enable=gocyclo \ - #--enable=golint \ <== comments on anything exported - #--enable=gotype \ - #--enable=interfacer \ - #--enable=unparam \ - #--enable=vet \ - -metalinter_all: - protoc $(INCLUDE) --lint_out=. types/*.proto - gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... - - -# 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 -.PHONEY: check build install check_tools get_tools update_tools get_vendor_deps test fmt metalinter metalinter_all diff --git a/libs/Makefile b/libs/Makefile deleted file mode 100644 index a55bc139c..000000000 --- a/libs/Makefile +++ /dev/null @@ -1,137 +0,0 @@ -GOTOOLS = \ - github.com/golang/dep/cmd/dep \ - github.com/golang/protobuf/protoc-gen-go \ - github.com/square/certstrap - # github.com/alecthomas/gometalinter.v2 \ - -GOTOOLS_CHECK = dep gometalinter.v2 protoc protoc-gen-go -INCLUDE = -I=. -I=${GOPATH}/src - -all: check get_vendor_deps protoc grpc_dbserver build test install metalinter - -check: check_tools - -######################################## -### Build - -protoc: - ## If you get the following error, - ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" - ## See https://stackoverflow.com/a/25518702 - protoc $(INCLUDE) --go_out=plugins=grpc:. common/*.proto - @echo "--> adding nolint declarations to protobuf generated files" - @awk '/package common/ { print "//nolint: gas"; print; next }1' common/types.pb.go > common/types.pb.go.new - @mv common/types.pb.go.new common/types.pb.go - -build: - # Nothing to build! - -install: - # Nothing to install! - - -######################################## -### Tools & dependencies - -check_tools: - @# https://stackoverflow.com/a/25668869 - @echo "Found tools: $(foreach tool,$(GOTOOLS_CHECK),\ - $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" - -get_tools: - @echo "--> Installing tools" - go get -u -v $(GOTOOLS) - # @gometalinter.v2 --install - -get_protoc: - @# https://github.com/google/protobuf/releases - curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \ - cd protobuf-3.4.1 && \ - DIST_LANG=cpp ./configure && \ - make && \ - make install && \ - cd .. && \ - rm -rf protobuf-3.4.1 - -update_tools: - @echo "--> Updating tools" - @go get -u $(GOTOOLS) - -get_vendor_deps: - @rm -rf vendor/ - @echo "--> Running dep ensure" - @dep ensure - - -######################################## -### Testing - -gen_certs: clean_certs - ## Generating certificates for TLS testing... - certstrap init --common-name "tendermint.com" --passphrase "" - certstrap request-cert -ip "::" --passphrase "" - certstrap sign "::" --CA "tendermint.com" --passphrase "" - mv out/::.crt out/::.key db/remotedb - -clean_certs: - ## Cleaning TLS testing certificates... - rm -rf out - rm -f db/remotedb/::.crt db/remotedb/::.key - -test: gen_certs - GOCACHE=off go test -tags gcc $(shell go list ./... | grep -v vendor) - make clean_certs - -test100: - @for i in {1..100}; do make test; done - - -######################################## -### Formatting, linting, and vetting - -fmt: - @go fmt ./... - -metalinter: - @echo "==> Running linter" - gometalinter.v2 --vendor --deadline=600s --disable-all \ - --enable=deadcode \ - --enable=goconst \ - --enable=goimports \ - --enable=gosimple \ - --enable=ineffassign \ - --enable=megacheck \ - --enable=misspell \ - --enable=staticcheck \ - --enable=safesql \ - --enable=structcheck \ - --enable=unconvert \ - --enable=unused \ - --enable=varcheck \ - --enable=vetshadow \ - ./... - - #--enable=maligned \ - #--enable=gas \ - #--enable=aligncheck \ - #--enable=dupl \ - #--enable=errcheck \ - #--enable=gocyclo \ - #--enable=golint \ <== comments on anything exported - #--enable=gotype \ - #--enable=interfacer \ - #--enable=unparam \ - #--enable=vet \ - -metalinter_all: - protoc $(INCLUDE) --lint_out=. types/*.proto - gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... - - -# 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: check protoc build check_tools get_tools get_protoc update_tools get_vendor_deps test fmt metalinter metalinter_all gen_certs clean_certs - -grpc_dbserver: - protoc -I db/remotedb/proto/ db/remotedb/proto/defs.proto --go_out=plugins=grpc:db/remotedb/proto From 14b60102406602a73c006115211d3ded130b65cf Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 5 Jul 2018 13:22:06 -0400 Subject: [PATCH 07/24] move a file --- docs/{examples/getting-started.md => quick-start.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{examples/getting-started.md => quick-start.md} (100%) diff --git a/docs/examples/getting-started.md b/docs/quick-start.md similarity index 100% rename from docs/examples/getting-started.md rename to docs/quick-start.md From 79d6bd8ce06b421ae37de2a8f1cab466770c8671 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 5 Jul 2018 13:27:54 -0400 Subject: [PATCH 08/24] TEST --- docs/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/README.md b/docs/README.md index 0ed3aaec9..1b558316d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,7 @@ # Tendermint +TEST! + Welcome to the Tendermint Core documentation! The introduction below provides an overview to help you navigate to your area of interest. From e54c0f804faa3d4a6c0c1ada0288c07d2a49ce50 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 5 Jul 2018 16:06:11 -0400 Subject: [PATCH 09/24] docs re-orgnization --- .gitignore | 1 - docs/README.md | 11 +-- docs/{ => app-dev}/abci-cli.md | 0 docs/{ => app-dev}/abci-spec.md | 0 docs/{ => app-dev}/app-architecture.md | 0 docs/{ => app-dev}/app-development.md | 0 docs/{ => app-dev}/ecosystem.md | 0 docs/{ => app-dev}/getting-started.md | 0 docs/{ => app-dev}/indexing-transactions.md | 0 .../subscribing-to-events-via-websocket.md | 0 docs/images/tmint-logo-blue.png | Bin 53596 -> 0 bytes docs/{ => introduction}/install.md | 0 docs/{ => introduction}/introduction.md | 0 docs/{ => introduction}/quick-start.md | 11 ++- docs/{ => networks}/deploy-testnets.md | 0 docs/{ => networks}/terraform-and-ansible.md | 0 docs/{ => research}/determinism.md | 0 .../{ => research}/transactional-semantics.md | 0 .../configuration.md | 0 .../{ => tendermint-core}/how-to-read-logs.md | 0 docs/{ => tendermint-core}/metrics.md | 0 .../{specification => tendermint-core}/rpc.md | 0 .../running-in-production.md | 0 .../{ => tendermint-core}/using-tendermint.md | 0 docs/tools/benchmarking.md | 49 +++++++++++ docs/tools/docker.md | 67 +++++++++++++++ docs/tools/monitoring.md | 77 ++++++++++++++++++ 27 files changed, 202 insertions(+), 14 deletions(-) rename docs/{ => app-dev}/abci-cli.md (100%) rename docs/{ => app-dev}/abci-spec.md (100%) rename docs/{ => app-dev}/app-architecture.md (100%) rename docs/{ => app-dev}/app-development.md (100%) rename docs/{ => app-dev}/ecosystem.md (100%) rename docs/{ => app-dev}/getting-started.md (100%) rename docs/{ => app-dev}/indexing-transactions.md (100%) rename docs/{ => app-dev}/subscribing-to-events-via-websocket.md (100%) delete mode 100644 docs/images/tmint-logo-blue.png rename docs/{ => introduction}/install.md (100%) rename docs/{ => introduction}/introduction.md (100%) rename docs/{ => introduction}/quick-start.md (77%) rename docs/{ => networks}/deploy-testnets.md (100%) rename docs/{ => networks}/terraform-and-ansible.md (100%) rename docs/{ => research}/determinism.md (100%) rename docs/{ => research}/transactional-semantics.md (100%) rename docs/{specification => tendermint-core}/configuration.md (100%) rename docs/{ => tendermint-core}/how-to-read-logs.md (100%) rename docs/{ => tendermint-core}/metrics.md (100%) rename docs/{specification => tendermint-core}/rpc.md (100%) rename docs/{ => tendermint-core}/running-in-production.md (100%) rename docs/{ => tendermint-core}/using-tendermint.md (100%) create mode 100644 docs/tools/benchmarking.md create mode 100644 docs/tools/docker.md create mode 100644 docs/tools/monitoring.md diff --git a/.gitignore b/.gitignore index be52e6447..c013b0e86 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ test/p2p/data/ test/logs coverage.txt docs/_build -docs/tools *.log abci-cli abci/types/types.pb.go diff --git a/docs/README.md b/docs/README.md index 1b558316d..16ea708ad 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,5 @@ # Tendermint -TEST! - Welcome to the Tendermint Core documentation! The introduction below provides an overview to help you navigate to your area of interest. @@ -13,18 +11,17 @@ replicates it on many machines. In other words, a blockchain. Tendermint requires an application running over the Application Blockchain Interface (ABCI) - and comes packaged with an example application to do so. -Follow the [installation instructions](./install) to get up and running -quickly. For more details on [using tendermint](./using-tendermint) see that +Follow the [installation instructions](./introduction/install) to get up and running +quickly. For more details on [using tendermint](./tendermint-core/using-tendermint) see that and the following sections. ## Networks Testnets can be setup manually on one or more machines, or automatically on one or more machine, using a variety of methods described in the [deploy testnets -section](./deploy-testnets). For more information (and to join) about the -Cosmos Network testnets, see [here](/getting-started/full-node.md). +section](./networks/deploy-testnets). ## Application Development The first step to building application on Tendermint is to [install -ABCI-CLI](./getting-started) and play with the example applications. +ABCI-CLI](./app-dev/getting-started) and play with the example applications. diff --git a/docs/abci-cli.md b/docs/app-dev/abci-cli.md similarity index 100% rename from docs/abci-cli.md rename to docs/app-dev/abci-cli.md diff --git a/docs/abci-spec.md b/docs/app-dev/abci-spec.md similarity index 100% rename from docs/abci-spec.md rename to docs/app-dev/abci-spec.md diff --git a/docs/app-architecture.md b/docs/app-dev/app-architecture.md similarity index 100% rename from docs/app-architecture.md rename to docs/app-dev/app-architecture.md diff --git a/docs/app-development.md b/docs/app-dev/app-development.md similarity index 100% rename from docs/app-development.md rename to docs/app-dev/app-development.md diff --git a/docs/ecosystem.md b/docs/app-dev/ecosystem.md similarity index 100% rename from docs/ecosystem.md rename to docs/app-dev/ecosystem.md diff --git a/docs/getting-started.md b/docs/app-dev/getting-started.md similarity index 100% rename from docs/getting-started.md rename to docs/app-dev/getting-started.md diff --git a/docs/indexing-transactions.md b/docs/app-dev/indexing-transactions.md similarity index 100% rename from docs/indexing-transactions.md rename to docs/app-dev/indexing-transactions.md diff --git a/docs/subscribing-to-events-via-websocket.md b/docs/app-dev/subscribing-to-events-via-websocket.md similarity index 100% rename from docs/subscribing-to-events-via-websocket.md rename to docs/app-dev/subscribing-to-events-via-websocket.md diff --git a/docs/images/tmint-logo-blue.png b/docs/images/tmint-logo-blue.png deleted file mode 100644 index cc4c8fb827987c5ca22efa5cc7a6362858fe4bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53596 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⪼>kX2)n%PhZ&%lOV5zW_^<*;Fs zz;8z`*b1u3E=iFY?Acb*U$AtoK^mJg8Y-hJYTWIl_u6t27gsasz6)xDsv`L)AnGbP zyK+FTa;Srnm6Uj?W)J_m@ZyvWXN4U%VSP(RzE zf*1>~z&)_Ry_s6_9BjaxqWM)38xN|@8Bv)$31A75Cjk7?9k9zmOQxZQ-*@z0L+U1> z#!F}>-B6Q}l0Aq@jXZ{eYwCfn4>7`TqWStU0Tx$hX@1qTLw%t6 zQdH&{c5m~DUdQ?*NaQGHLRA!_2H<#luO9&#po$2vr!y7xH$#{O4e%$NaZU`gsS-zH3rM* zy>7tX0iHzf-2-(QknqRWY_@Q7beF8wE|khr27()a-VrrA(0a|-1k0;KG~YeipeiK% zwKa!5T%Yf+`5=``mRXBY==fx29C zDXrHE+`W;5X}-J6MAb<6(G`Dg1Mr#z(H*s62_TuBxXa8v7N7=VZF;X0ZBgUob$ahU zjZsw^DXVh~8@aa(6D`y_l+0f+4BTWVs6y}3dW~oY3#_3u-+fl1s`RL^&==Urz2mfK znS!f>WC|Zm)tORIi2`^}lSaTEV*LZX*CB9?Ho{8xvzL3rhq6^#jnerSW`Wy00;Cwva7(p*}v3G+}BnL9;2WezNK#rIv8v7>v2^o?3OxN%4iP*;d@gSRHYE&<}F zJdY_s)ha^#iO(n4(>-CjaCv?OQ~Dxfz#~S2>QqAOwE*_Y%!l;eV`jr*cbvG^r`XjM zo=o?mls?H6@DS7so4shg4t#`~$(i(CqvaZ-iMUqY3G6m6N!O+uOzIm90ZsnJ`v%HVRu<#uaB(HWX8+=u?5id$-nAW5i0Fpp$zXpIe ze2Xd9Cv;wMhJYrr{IPQT0Na0M6a&;AVuyDPU{`zrOzc`tf3N5Vs@FZVUTsiktuNAg zuek(^-;g>0x#!s9U2#ahB12$eBlr%mmwbgOSPq?6nk>*6z#7~EV1fi#0KbWldzxKd zseKu+&xVF^jFaC>KraGBh0%JI`4Tjdd(wIn9KrRQkqEsl+2LgDKg^bY5YKKohwy ztvAJZT+7+bUjXV*hN#8eIK3S?^}XkHOwESVd3AXdv;&|Xtv5y8XjleE1wj;FVz*c1 z$5>VXOfR1gH^XC?qVdT$b=eM@$P;P3N!H@J&PI&lne6x1vkOv){W!rRIPuMZxkS7{ zGdiy-e*$gg$)ojVmjIG*G29#@xf?rvIYI_Q%5V~=_)X4xQ!K-jZ4aGSlxEerl}A^K zaXGvLB)LsEyZ$x*Saru#VIWR&3a7m}?u3*rgT^Zg>U0&i@|Y*M0WOHML7GSQVc)+> z{q2|<%*RPC;H)7X>)pcMf0?DNKn3WF)7*n|-ZYrgw4qsaUPb=P ztvot&5tqflW1I!-{=k2!ngc~|=ae_UfP?vn_70s_5Y#gOT*)VS2(r*?+XO>ju4#g zgPiRa@jFQUj?j3;NXE?OpV4^>0X_<7oPa9Nac2KtkMz6;PguNdE8 zCUQ4AZy|y4z)T(um0sk)jR0}Wkd7||D&NbwZW)6h736JzijfZ)2KhAJQZ@rac?MQ` z%0Q>}|9>um8r}sc{Q#%BWekN>kS{r_MK)$4Z=my*a%)3iDsKjhALp$8|0>q)mB^hB zoaq+QA5p@ZjcB||?1fC^gdjR^EoYD|=^n87^EI8*{+rIndYug10mx?nEoA|uhNo$~ zLgWQF@_$Afk^s!*L1^)*j(Hkj=?7q|TLZ;^#A$9RiHIWhr|$~!4rC%n1k-t|*???I zW8w18I-~!8hW7bwr25XB=9cmvq>5=YUL7zusT`;A)-x5D%#HuY-kE>*c;#{YXeo&# zOkxW`5VRuNAcLqdL}@KiDyr66f(8?7MHQoJ$+3^6PUwuKwKvl$hcU6MrIr~=iw>Qh z4yGJ6TBT|JZ1_F`coWcPa-9}srGikE<~1(%R2?1LOOYv z#Jge^be_7yqTybk4hF0@K@XThp>oz%WK5HHIK9ds zlGlA6PAL(N9HwKbRJqr$H)Y;B}Yz03e?{F{|$m%AD06(ZD`&RqZ z6rbn+-6NP>UV^&FP-DAeIpRTZeR)R%`C6)-yt^f?4s@hW<+3sK8DFRW6I{n-R)9WP zdRBey5XcFltK=O_cStMWk$A`KfzDfd*>o)Y+Q;etr#WOAu?s1k9qtswJ)*BlJD4tr zPM#|7&cPmr`!1c2JB7YY|EG_#IlKV%F-lGClnsbbe<$x)9z~@7y1YB*73}Oag3rj7 zkA0f{3op_MELjn1dcMtXx_^OObaz^mB8v|}0|a$P9z zKq^CyORpsH7GQU5l}_WdGNPb5_w=9mWj9K3VTn*JGt|(|sS3z-x3mNK7SYQ2@@_&m z>_kTUsb}Tn>%7x{cIT#~+Rj3~^b1o%JLei8*oHa|7zpCjf$6d z6z35C3oFRG8Bgmaz>iVw7RS*5r3>F7?}|QHK|9sTHVg!W+gaK{tVi^6b9uL80eEIh zqc?PF>r(Xezvg&jdQGudMfz^Y>ZzDkcg2XmBYJtGyc+_Z-KzTFs832Z@HNT3Sso~Q z16xLUYGf-C^f#gSfH*FGj4O+@_k9#A&doNT|Qdg zP3ct{J9zi?$W_e3yE{$3oUFZeT>m(Kyu5`iq1NhRLskP4ep%W9Jlqcu^>leR<}UaV zz+Jy|wOaI5c6paR8n!EF-@+>;-+(LVnwr=U#Px9Fq#Xca!>6|M+Vd!Q{BGc#u~u7h zS37wfkg#LO(fZ!$NWj)lPqlE+<31R12IZ2=J-pZW4i zR$X5mWPqz@lp5HUG(gCkrQMuGfSfDF$h$qCgNN`?j^F;;44O3Ke13YJ|Mh2QKDqqx zj1@!u{>nM}eQfQNZlMM?W{G{u9c0~{6M%01qqHN~1YQ7Cj|+muURfFUXS{cEV8r86w4 zO{Il zKsQ&Fb_^5HF98lH121V=9$YvFm4Q8ggP$Llbz@Fz3t%y#EqEL+TTL1G2>P*&6atLb?BhS?v}4(c9?)yYD+GV|0$nag zlmU(u`@D50upKi2{rp>L$1(!`2wb~L5R!B+r7m4Y8sMOR9qGJta5KJp2&Zp0Zt zLpPFkI1AtlfW$bZ-~*fCl`=*l6t_Q#*|KiLe*1p@TGk0z3m@0NR|p<37+xm>6#^K* z9pY_d-Gmx|E52q*I|1F`1AAzgQlKXWerAN`0yu!{Ly~3P1i%;JwUeZsfg$)6Kx3uA z&Ix#ttWyYZ63tUDlynpF>>D~y);UOkkL|koo1M<^8mXxe@YsJe6do_@20RSd4BlVb zIT(+B1h88vP}3VEE^0+zjM;%7-sdX?X12o%WVJ$Ipr?88IxW_M zWA+_AMAliTRt7%4m+7V$c-aT9kJ35=kb>jp$!EsMx_hSDxAbgTr(qTT5x{dwftG3b z^4OphC}?9J{ZUzW&LI0(|19f7?8Oi9SCsD5uuvh&JpGZf z?wFVDTl%!DQxWq%ewcq!F%Yr{Ul>)IDh3+tZ;gE*>5j4A+8LTA>s;(2cnP4Rq4-FP z!qT%08+3hfP{Vv2bI=t3a3OtSCk3|Xh+F9c(KqE7vwVm?7lq$ma|#%RM> zz-Vrs=+lx;$Q**veurWp;xa)+#3}|ZN|m!N0o@zxlsNkh;D3^JLe>)u_o1!IfrkzR z1@VnyAmgfe4P20Phh*3{^&nYi|@*xw#{SkfKxsd=7HNIEA| z2}b;SIwFdMO9a(WCt5kMQL3L_0|kj+ns;@sq;v9fhDQJ|DF+G~Fcd?3<-o^z^D<~4 z>CQ;C@9HdBCxzjmZ)m!5ARu}&K`9(j4umw*&Y%Ece+-~*e&rqY>oBYUbRG}^g#?8V z)m1qVQmU49A>>QCGj^J{^)^YTWhKJ`Ak;@epxMk&2bYusCFWO$mC;Jj6&37{0Zf&3 zUOr$L_-pC*7cfT|%HXr&V)>5f=t-qynHN0rG)%94mnSO2QhB4S;PG1UUz-OCnv)9Z=Ic#&dG+1Fbk- z0#s?CC~R5CvGUufB*-aY{_?oDq!&3^=AFGx(!Jmo$3&oS!ENC<0Wfq-Nzik|x+wO@ zd68p(6W}aK_XCbE0YV=r2}|Zsgr1-v__<_V6{F<5$XQ|?;uCW237sN2Rsgw*!jAnM znd=3B2bjMl%#4xrA||P}b!&H#bYB?8F&TtbC<-e!b0n^NAiN;kx-N3$yoh4;!q zq$(a}<7VV#<2+H?m@n>CNOy^I zL?(daiK6lur;+vDB;g8uth@0N&J$&~b$|s`k?s=hiz9v&2Ew@_QLiToSD0XxpxNl<4poO6m!l`n6J?6^>V6aF{4V6rBSF@R#LQix zF0s+L9XaJVPmuN2t2@6b(j6mLWM^2EvxzV&kFF|n-m7Hc5azptuflnPY_^VX7tWpI zbd|4$(iVhKSu{OJB;Dic5>vZyS^ibQ-PQMkow z)Hy@4#8<8I@QT!Zq&c-(q{ z&%n8xtd^Mo))Ge2tWwGLo)W@vjwg*v!a0$NQBr1{9Tx$)O1(mv3E)i9NS31ND$$-m z816CCx+N2Fo)ELGqdOPpt};hv04VN98cEXNVwGfXB@72Sw6<|g_TW4r?qG`#q{>E_ z383Mjq>&(NRC4V?7EaQEpOKET%laLkzBqT51CEfH0LGCd)mFX&`BfH+*{0^_>8hVlfkKEGix%H5~4&?A6mjmaYecIhN}A89y^^{ujNP@Xw; z2N|b!HqJfZQK1Q-Z8h>pn+-ZSE+7rZG5#~DRy~ww%=^~Md_2y5V1dvC(1hDia#xUtC-k%}&Jc`e_%k5KHn745IQNO?q$Ys11d=LsPZG+lRzCvq zgeAt+nTYa?*lL{DE|hyk(_*O!;6f5fk^V}tT|yqdVEtkVFO_9RALG2Hq1-PzN(}(f z`y`SQ9h5@j6;6N1HojV#`+zdT`U1z(QSKT4kQxB;enBEBut6y^HURj<4%_nVz<4H< z7{AC>P!;FCu~=#XDBDCLKgxqjiRJer5RX`C+n(AjP@VzSM}a(za_?v{NNNHo7(*mK z!V;;#*Z|-aqm1jb5ak)L#W=0EP|g%t$@Jo^$sguDskk@+z%LpYA20P3$}?cH@oN7A z$~|Pc)&wx5FOdih{XYyRgZhT?o59;Lo&k5;hSfS34@ z660xOd<;-2TL8Reo7e;}mI>hhc|t3rqj`thM~vSx9f|RDXSW7SU-$;v<*XwrDsDJzd^5Uht2i++WJ`#J*k@k;%82QC%w_4h8X(PF470*$w_> zyy7{h-Dkv}FGtaHWb#eM7iz`B^FSU3CcOl<-f$3v$tC@K?6W-Xa`%O=|@H7`MVTHI^A=4YVX_my8oZUSgRDxZD-4-(5| z5}A0+6y7Frf$6p}74TW?JzLcVfM`v7Qpp;s_WeJSOnhd$ZJXY}cv>vAjj031{b!!s z1aK-32q)enmdO$P7T~94a#UbEEgrCqX&S}@;AXiA;FLo{|Nbvfi)20P#A|vOzwLwZ zbm&yeHl~Fr4}g-^audLbVc!lsbhug|kFZQU{}5I=-BdL(r)X$i&yVWr*#Q2J`1S*%yv{~xuk!wT`9?zWxcEWmUy-UIkB#zSF&-UQIBD99xK zRwkFkEvyg!X_I5xsnr;dPh;b|!CIj_7~1Mh0L3QIi`@rllHG4dbzS z*?1!s#>1gizTN~-czbX$%*c~VffE43V6ttkxDhx$#)ma5#dtuB(i;Gx{EmTzFrtQB z2G6iG!eEPSudc&*Y&P0Pv>D?e@s!{M5OohKf+zF>Xv^)4fiS_gSf64%Hkb0MCR85l z6eoa?PN117_zZ@9K%sD;?d{W#V>~YPZ1)Gi}Z3ANaPk>C5 zoB%fB{U@MTd)7lpIy~WFR>N^<5Saq6}P~6EFNa_HB>$=dPza~y`9?+I z7qJvV<1*WZan4LUKD3ReBgVsIK#t@D(AwL}0e33u-o`=*j!m`|n}zXM++dnc&88R+ zl=CGAfT-+rFKt)5R#7&$wFXFTscFaNVLTSL*EODs@L>5^a{}=4U+L_gil&b(VHE_( z0@IQmkMKAgQp+@*br=tq*F-0P(cYPE(@xQI9;dVg$w1SVaov0@I@*2)yoGUo3dr$+ zXP&#Zl+=6zDda? zdD}gJ*%%L*zDJ8r01tU2d99}6mDE2_sI6`wyZdrAFI|fA52yD5 zGv2ms$6!2VZ0`#%Y=Ci?syYEUzkMk8YEiKZX?@6)l$o}Tg; zd#8Huo}yYWr}JU6gE^kh;=NpnaLCH4(^hr@nC!6D&QnCCen8~|=eMSPyA|Q7QqA_r zu-h;Wdns=&I|1D2>~6J%2a76wk-~?L?QwJWBRo}%&uChOarlgrWe0$$VP`j$KC3FU z19cCcdrce1Z2&1_`w~E{a*V_0%+#F#ieGk7=u@gX+4lokjcZs|F7FVGr^tNM&w}F+ z4qviDcLIoBaZl&#sxDg}Mb{x~u4(DsM|g@nX`0K^2#2qDPj~`w?qz_7R88hhr0I~A zb&hH4W+6O9Mw;gGXN1GoR0vN1ZoLf9bcd|S3G^KHGRd@cXCOR98k*)Z2jQ@f8-yo- zzyHb|j{_RYYGlR3=Uiu6yCV^v8s+(>x!i(r_y+xjCxB*eyJGQ)JXwic{Q+NK``Qne z&Zfu&(^!UA!#I3{CS8OlfGU@{A#snc!huvAzMw^YzG(!py*j)b!r@yqZYewgM0dEL zaFVXScPTi0#aPqo6(Kw&=9#9#iD0lF6Kg9^02}lLPS&-zlX}C~Y&7lOGK8mu?aLYS z5f1zDw(xxyYqh? z>pYI*4~LGEB|9fc)?@2f5|T2V?40aUYD!}iBP}ChGFh^ujx`=bnB+7{I%!m6s0a@e z5+g$kGLdQ8Jf`KL5ZC-TQ?Bc}@B3Qb_xJ1Zdj13V^?iTe*X46=*L___>Y1Bdvx;;Z z|1(G0zC#3_j4P#gW@Pjxahzj{(G!4^Z*Fp(N|_s5!yYsn|0hpczg+~LjMt@`axsC! z#b;(u04q$*ZM(stmFEF~_=#NU3qUszcrq4AFA7@;9Bw{0dji;M<_m&1nw;5jT8*Dt ztL$G+CV?CGx7Wu64p-aEo&YYs#k|O>eQR=JchG74%-hliHYV^StdZ`?Q38j%m1a)> zS({A@Y}z9x=e3zepJFx+001BW zNklT!1We@A}JHW)IN-qs92poRqmNR_< zI9WOEA-!gHLi4CGJPeXH@f`wB=#X?r+7LK=Rn0Md0%(%%U$UGsJ0D*74NpgPGfmm-F+7;ddeUEbE8b1n z#+3w~$j$sb2&UO@{sd6AAT7ykG(46|o6=tR8KJD>-^n|XUefEsC;~?kHkm&GIBU}I z{pozeGpWK0fZ=bavX9NlJCU=}3&aEhM>3{ZFacB_E)HeU<%TCxA&=(5-}A~s*6Bgu z320x#em8+5DKAZJb}lQ+flwUfg?FvESLbC8+3W( zjx;`v`)Dl^=`5|}A_7m~FVcIb$s5V~%7zJG#1*16hQ4fk7Cht&iOrS1#Ci{bCm_A9 zv?gyPZL|#&K%JSAEb`4yVik==Vm0b1OL>OC{T0hrZU?UdLJCINFabzPz0dp@I?-1o zqq?${JIUK$p7bIymB5jL$yQ7N^ZUjeL)F{O4`CB+MN+O()^a&{`+G@xm3WB2k%}TK zCV*1$J``xI`7cLs{y6F35}$#1*{X8hWR`4!B1oDk76TtikUp{imiWP66ok-2s%6>jc-hS3gFBY8% z9D%g5Wdg{E>fz&!Z?$5*E$AjpKAA_5x1Zh0y(rm~z!A)3TP6T!WF+?jewby&V!8hx zDJxJm^!f`4+{Y5-UUa*Fz!A_Nt(gF7&kDWR`k&abRPO&n>ON3blxNTQ8l>EdPstm0 z%&OtM8}hk;y{@-oohN7|Qg^kqq&x}8&v@>~fF<_WGXZpbD$GKQELmj%okRe+mn&P^ zi@<#>S8m0h$QvOXwPylw`UPI)@a2{)aWjoXAYGI-{TF%r=&#(0e~>rAD6nV(s2=PG zfi?5XTe862^bx@fR`!%v(EF%gPPrAl3>e|ev}ghd_Huv|wybUzZA3uQ7tq{5-acP9 zO0OBm2^``4)usvHjk&2WuvUH(E7o>DT|`jQH++sKZ*N1CJ8{um0!K)zY?=Vd2YF0R zwT~@W*#k5Yfo)M%bs2eko1@%`-_;;+gtfkuO(Q_-0|AzJe3{{^y684~2ur@l`#pJk zYr#&yGRJdlngDVFJuByIOOw}BX9g`q2s4yztyhA;JxPCY+?u=*fxH`SngE=UZ3C>a z{t9y!ROL=Oh%gS671p&>TLSkaec{+f@kc;)*bi@ar*q04D_GYv#26)#m5 z_H_dHv_rWOMdXc0v~OnB1mKJebo=XsYfN0r!w2XeLXtjyhDQN<>!92S-W-C6)W6lP z2_XAukX06MRmQlLTyqKSLs%!3mE}=@-lTtHtRinj^(A5-yCwkV)!{*wIs6{OmeI8x z-9u=PDLeZad3(u~{$C9D4k0>AEt>$!1iKE+UTo4LI@F|i2=9!tw4al=mjhYCeJFh` zfg?KOEt>$GlYIlN^RAnWS;1CXhbTy2?LCpay$n_ELrb0mgeXn4Z34)8A<#N&7M3>S z_}`>+h{R@PZFi8j7wL0Vc@7YwHO{sPz?nZJ&_bs#FyY`2(>O$m~qaP_Xa8?wyzRBR@#r5Rp7(ar=|Em+i`Z_=3CN!Tk3=X={@dm zXd9w(o$`eqO)3$%htJa?qQ?y z60(H65xMmh?3(~;-xX}F)mEqN!9Zha8Y1+lvb~bvGNOY;^H#H7286M(ZI z=*>xUx+)K|OH&Glh>cP<_*wFH*PuK<#=!Ytqm>gtmD54jd-i47LH5a{UWnR<%JXa- zdAsYY+=T(;joAEPl|>sHMZB&9O1v|7NYmQ@+><=-tO*I?!t2NMtoY>IRQBD z)(W`b%)(a`|4l)&5QARIbL?&McDG2m3vZG)&X>O_qP)fK#h@!5xG?tr!s|z`5QoOf zQ|t}$cK5yXPaX0`oI2lV=?L)ccR`oj{!qN{^FB?h5R3LDg(ujj}c4Mra0GtKEAGq`SJ&_;H+MbjN@o6nQvodZb@3e1%)0n&wza{og0NKNWFFWsW zw8yga0;+@4k^zo?q+ef zLAVF6IA;e7j6ZDVB+f8NVyMS&dZ-|uGYV!n8F3iiz4lJw^_VX*eMS~DG z=|kw2khhz;8Nxj{N#2l|X!8W%?41x|>1|j4BGiXp+?V2#31-E>y&!EebMLJwuxJp!zo9%At&PVWu) zv6E;H6s9Rpro-gzW|eXeR+BfBhIX@i0w`TrKg{x{{3ghUo}o98I8au2F1<Hk*X91GW~^T(E#Fh zBYhk2OXLl?0_!ILXGdPB1gy%e9-Y$-GXz;ZC6F`|2mxK#pQNs6r4x=fM`kU|s z%C1G;E_MjFpj2=2Mp~@2e*$pIwYoA~2uIfV-3*ZRJ~e@s@_oQ%+mpA8!OAOGG4e)w zyl@^&0M5g&T^KHfwxc|s4|v%$N&>M{%CqMk@^;ZddHuS8ypble%A5})z!PIb#!!6o zN%vnG45T7Z`!9R<9@pd9#{qnW#DrKvlA>WkNOIT=Ybb3dhX`qM3{jJ$$$425<**me zQVuPmP0CV~Lycb9au{PK>}gJOD$m2T=DD8X+4JJQJKeuu-Pd(r*XRAeU%5V?>-YP1 z|L(&RHTTX8tamBAR7_wvmK&%!n9RcjKt4XH??UWIlRq!`5c&b?d#L8lNymDZVy%h^ z+`w`JJq~$XOaLS`Lf@5$?B4n1myAa{fP&L%?i+8cw_J;bDkcz%^#+=r@G$|9tsC`T ziVH7X6HsqIssS|gP;=KTzU2cY&2_#~@fxczN{EPt6A0bcBn#Rk6DeM(@EWBL0nhD@yBVd8&Qhp`? zavmc9Xo|z`g;aLRFIalHU1}y!i1h{*7`bpX0g!KS6(4AZ!Q5_?_Q@hFzFdTw3HW!! zf&&v0c$xsnKSGfxXhH7)OKQ7h7M5S`sHzbR-dqz)4lLMc#?=Tg{W>BA){XKRM{$oh zApp54Du>WNz6KT@7|{L(R}%oq%)+C#fMt>MQ>g9`7epYJrFsnZB{&KIEI7p11VEC{ z;&I!+s%deiv`?2s2y*chg77$v5degoeydr7-&~O@Fv#<^3H9A^3NgskSwba>g=ca5 zQJ`<7J#Qm`SzHsu3XJg!VPS7G`V|XD7 znO1Jp;;>rY8|x3W?dER+AdZE|6&Mk8ij{2boo8lehAv0|~)kyl@?}QA1E`vM{CjgQ>7ncM8`lbxh(*ft7z@dS;C%|Ha?K!oiammGL2Y%8e!$>fNO=v(NUe-!Bb$sp$O33e z;B*2YeWtcWx}YM9fMh0jLQ~u&OUCa!Gv_V;jx>PgG0S$mjsSK!_>v9OGAyt)qx+!V zTS!Oh{SEBoli9@xfdIWfT=EFFqAws{P=WJJb~i!&NaQ2?v*#L12@Re<6p;WmyZM~} zNT2xk5HM6L+mauc-UFW?BB{dZj2)%PS%FZ1mf0R0j{qZcK19M$ZMM5vGrj|GS**;0 z5zM8O=Z#o^5~Ib0XaD-F5i*8q`6SW67ihf@lhnz94CXZ8GdwOBD7j<8^$5_$eI7D~ zD&`uWsfF@yM^aKjotaE(;kLJt4N%c0c~8j7Gn4-P>Sv{m^X$V@7$4cn2mUbGS+f%@(m@;?HY zhTv=f(AA7f9kf<>KcpsA63l#R8PYRxS8o=+!vS@D zRZo-=z}RIK?!O7;I{0|4raQh15lSsF(^P_64!9ySP*K-nB8)aUn6RNN76q|dFZdj! zD3#DuV;S1b!f_nXmF=yaith)$n+W8G#C~WA$kZzQ&P@C5-j5)q9@}Uy#u2;QBREj* z-7}Vi)Vr?qpLwQ=Gy;%svU;K_@S>G&OS!1FGoqB*rpZ$}>gPrvI#8t3+2;RiPrl-R z>m$?zKx_`5G(utExzS1Mcq#T8q$%aD)svj;eGVc$sH7GVS6?W4bHz`@P0ti-1R(9M zgttI#!1b#Pj&6y01c6FDsjJ;MiyxUIK&aR~HxJ35xT}=DM6Ey~8jrYiR7Yq=p-y&O zRT@v1lll5pEeG1yANO{G^1Xexw8xdkDSn+@{e>F=NT&OTD3GWJ%{HcS_e{*Ah?TDa z;OQBG-J2jqC_~H8Kg;-ztNCc9uhr3?2*@j81!^RmI^Nk<9F}<*l9h5Z)_$z#hv4P_ zP>R)O%6VRyO_1W(3-=Il1R$x~CZS6refs~D&uP&wBV4IV#ykYu^n^1`S%R0mv8|#L z_p%kgrqLlHCa(m17a)iparWHS_z~je@d&^(R{40K3H&Yc)~QN#W~}0y0=?qgM$i#} zjCJ3EUhys_Fq!8L4?)6GztrX@-mu?15h%Pe>hiuyp4n2jK=F&)`V(P=jlr#g`PC?V zkv-=H#sDCb!&k!V0&sT#v6+XSXsw_yAmRY=d#6pg{W>8vX;u{FlNK^2O^QI za2ki7v(lw50m^@rcORig0AiEZWgZI0hh8O)rlR!r7UGurgU_T*^DYiWxB{seen{s- zm{FY+|F~LXh~R2pOY}qSv~J=9Uy(}t1rnFy_mrkr9Sd+AKnxB)7Fp?2m65BI|4ctG zB0PKl3$LMlT4nHK&O3q?HXf-9mH$5GIr!jz(ZXLZzJZpqB{FnTCYnjeHdI0CJHaYo{6XTxI*47X`rXYl ztIm4ZzpukD6*C=gtuI^a|2rG%bGkZ(JV8w13{+r~1juv7>eQhT;<5V9Q`K zC}&D9R9PJsJ-SDAK<7%~cn|H6aT2~39wAe3X#kr#9lf%aN+(S3=<@5{JB2sdvJDKm zGpQyzU9H+W`)svC)k+n%2K~SQP&z}e{0LWcfhh)a#`xB!SBYGm`jFKCo2fj5k0p3x zqg9W|LH_G-wtrbt@ECy7<)RzU*=>0I6K)j52HW%p6KFrGq;HT;$6wE3Csnl3QukiQ zjRLCnsQ>J#PR-D)a-6Y1OG>v1<#^#ue?mZvIXXd=(Yr~G?U-o8VV(X~joE^IR5AN+ zyF24y-BqB`!69+JW++s-@^HmhnTk=5myL_w9McHBprYpNC!zN4>(m<$Mk?>?N}?qZJmw>y;+1U zR9R;0in7LI3Q#KY_>vQyarddpOz~&YmU54{rpKZrrLq1mdw1g3Q@+P>{1HXgT!Sl8 zNhDjFq!e|FD>q{(TMWuNNHsJ?k=)2wvPHJC+_@>+$QWW6hENzrHQBlhBSe<5Z!?cO zGmpp2y~@(>{I;{ap8vt`^ZmTf=lss{J!_wC@}xg>!gJHjGfhR=1y0d|e$X0k0;I~V zx3SMxcOz^S8+X^zFQbcn`n?* z*T6GU*{S@5rJ$d9XBfc|#$@R38R1@)Ny)DaXJtLjnB9z~Q2E6DE1E|I4;RxiZ&x*7 zPDGe1oud97|23Hs_~ZY|u_RvDFP-m0Y=oP(?&C3q4U~H|pwr&yiI{oNH|wWxc)CMnSZ>v1P@HbnJGkY4txEDAeVQnb?B zg1`Klb>brqz3Q6Tg+5YUvvG4aui(|C-!5kcFT8ntY8_dw@(6)7xp0YuXicSN0gE$w z-d?!CP3({7ws#E6a`2y!lE0%C%ccc8!#MnEK>zKulF#u@+3%?aIQ zy$4>t(C3RndCyk+Ozew8o=<)e8`jE0JP+ws{zspOUC(d)ByN>mQ)AXhd+e?;yEUZM z4cbW!zDaS;kAppx(=UdL_Bim$(x(`{`lETB(zs)#ISuW5-|~0ZHp)Zd z7yE6;Cq|^Loa&_L-l(1_w0;Q_h=N#EtzM`8jxJ4nTRTlNwYU2pQ`2b{)>C|&nAERR zgF~VSJ?q7^){1XC(p;KW$FBHg;ObN9A4u!nrS~IUMyKYbBu(S-<=^cZBD9jh9jG3l zF=M}BQfrONIvPwnDs`ECCa&l0`7RAQiT4}mX0C(gMvci^xyq*@D^^C1;4`=&LIr5X zfKsNkRz03ZlWA8!i(MUdUrwEOG(Evm3@cvQ=CdJLrCai1*5>=NEBTvQ&l6nYWOvF3 zsDJFd&o^1Le%E(m3mQ!~YMGe!UH>xMKXr6gs8>Q`o$Yj5t+~g*S?6LMoCA0FC=RgT z-+l${8zs2nr}@k)pq?6O{h7}?;K9Z~`q0LDNuvkHE~RapS(=>X^66*o`Zw3e>29^U z1~v(^x*c73@lfiBoQ#xJ-R+z5PhS{+!S5bddDM^c0mALKWj5{(*P;^(!-FRE+7tTl&d5Inr*|8&#${X3+``1zs9z4PPIZ2I z_j2}$17AM#9vkq_*3GI@O%3@XQE;ei?y?MkKpjUhmvv|xA2kJD<3Oa~P(|*ie1HHc z3z*3|b}*l|1InqsMfhtRdt;~{prQxi%ws($aiaBra@@0ohic>z>IbOc`a)*0ezbkc z1L8n=FB62PsvA240TuQ4WCpSRc;>JZ98gZ}`)0yZ74(b(0u{N~Hku)uEldlHn7*R0=M3ElloQxVjZ`Hj zQ9$4gEOO15yrNOawg5mmt^L$V)!A`W5O@uzg1SsyQ8{&;t^~>&H%XmT&G)8)KpE3Y z+B0!Q=i=9NCQ#N@N42g3a3cWtf2OI7e{u4&r#pc%s&7>5a^;HNlo0si-+ux3{zc2^ zJf{Fax&JH|)rwWuVU!T~m0jt3j9D==doh461;v^q0Wv5d6l>gBCaYwam;8;61zxf5f|{`! zHGwa0K#_(;GgYN^3Q)k~fWV*rQe>-vSS6&@p_EXX6c;9{**bj8&Cfp0b5fSbaZ=Nh(>@9uK93p=_-)Ej9g=?PK=c zh9XX#{W~*7r7fOg6%@>8LlterDtZ~Eh1|0fQI7#CeTCoA%21{y=d~8A&=|@K8SDEC zvr{JK_Mn}iJl?0Y8Y{2tHq;pMbTW&%DI?up($Y|t#o9lF*<_khXUI;~s}Gr(GUN5J zF^vtyh)dT=tjvBKORXU*U4D*aUdqtG%R6XpD1lY>Al>CED1?GTHtZrSnUykCZ;S;E z4%z>M@%Pkit_JOINYx<+!%i_K<;=X4E0&P8{1OA*=gKu=36+P$RgDQ{M9SV--U@_l zHA>c3t`7F5`j9XSe{TINL$9lJJLD+toIZ0kWT_QP0BPE_)sV@k7=3cMX$hHWINeL% zx$=0#2e=?TRbS3!C@PCDp|d3+@RGBGY*Jh=+a z>BoXVDt7tRWfm&bq&WH?l2`kxi@0*tzaXDgfdp91L1+<=000c9Nkl3rOW-zlvvhdoW3WR9$yyE7D+nROZVX+`0Q_}eJkJmVNfN($(n&pg?(v!ANzhKEA4(ms^Wz^yAUHLx~fE(VH zbB-EPCU>;us$ta>LgmQa9Em28JCq@3C^SdTukSzc z{ptJu^!)U`pXdGYd7jVvu78(>NS%Z>j2weooO8L)jVTZOs$G~*%E5>7RsK7k$ZK5c z+a+r*yVwfb;EK_A*v9J<_E)XTl3j(w60filL;9?Ss@-lm8_uKl`8Re|H?l_*QRvmg8o6ut*tpydPo#Vxu4Skw&Q$tB zQ=Z&b@P1Un|`lzM<>SSig~9{pX_}y`jSs_kz(dXE98aMsMB+QVVPbvx#B1r zK6$k3)=BRwsIW;Y8zJ1Oami=6-11KTLeAgNv<~!Mbwi^^^g3a4kHKQS-wZbxRzakL zVo)NkgzKs#U>ysQ3<4fx9!D2CR>~4)CyGGBNvb97UdKtm{`!sPhp7{#M62EgK;B=ziqhRYNm$G-;F>@KN3Vf;|5K-OGGW^G&_hpd=+ChNhu zbI%+rFx(7u;j}>sK8<$%lIENllMsAmudVmOM8UGf$`oFCa-Hy;%r##!e__$27e1l6 zj_2p)=yv~5YWPBb^k#x8@hdm;i=#i^3KQ0Iu}JEzl_u_DC%ZW21Q!M9aU%js*e5n! zy@V?UE#VT?5eX+EhtfK%KXFHlzmYNZ=Ycjqp4BZn&HrijToTCtt5fbpHXh~$qOBz1 z4YgxbhMX0R<)6_8&R#*$FR-TX?wn+Z0}eFXbZ@A-0y%DECJbjk{gbtTbx#{&lYKG` zW^qPAx0I-EbO_S8bu>&-Lu4Cy0RFwZ*(acj-q73A0yZX{UKhiZ!9tdr$-FIeu?$i!ajG^pp=elJu zCy1dVu)VNZy6u zB#62{8H9QKXVH$Q8(z3vFL@dyw^yq;Lp+Zcl$O2w>-aUnf(wJS7!;l^uA&d9F0+oa!?iQE}=+ZMhf|e*o6+|`PY=Z$**-cc%%K&g| z&k=l2-zfL#pp3WXbGt6m@FLUIfd=m?s;L-P{N&mA1g|R!wknhh`650VVs>1~cqTqS zCaXWD&CO9-^5ezv*qa_#f znR{}gf{#7nxN|y$!}igKK_do_PSXQ?QgK^4fC6ir?>?gJE#_|`#TYT?m?_NgT)uf9 zov3A)IO`Yp6Ln~AWM&>u89+J`v$TRGvs!dxPKo7@7sZ+he~J97h2@u)Q>9L*eBa=9 z6^;;M)AhPJr3;%QssvnQUv2;P*x)xb<2Bj$X(@9_lx-%k%$F7K)$z}9P~fzMIyfmR zF_6$0a7C{5GEeV$RO;I?$%$y^_A$)~qd9N&@|XT|_Ho(Lrd(IkElwm4to8DYd~y3h zHje3exWjDaVriY^W4?Uu@NE@!{w&#s$|hga&k6eVkDpWyV6H*!%RphbOmA^bD zqECA_a9a6cDMhNLkcBq_7m<)JZobLD7BvpEs6NvXJ?8mJfQr ztzSNR`^bE{p(85il};oyw z)O!bQ6tEujNm^6_=KD`oB_rigd{|9e+YN*kpuy`+riRW$YO7O!4k}~{YkKSKsA13h z^wW#jJ8}W3y7V+85SJW?OpR7EeY`@7GQzEkNI#S{oeQrT!opL(L6&$&UUtpKoeBLQ z8FMUKF8<=QfO|9~lCF1*UtpRs zEsP5EwA9VxtqsZz>viLG*cuv#6{eW*j&VR?fbq8x_&w(f)ttCxV$%YVh9#@bYX;1; zA8}ghpo`K@_7nxbZxA}N2Mo38EczQO5XFhh;0X9k-8%G8BD^SW+l%c8D^>gH50&M% z)C}?XKp2U)+39BpRxistR$XYPZS!i9xi<3o#Ao9&3*meF-wkrR)3@_ZXoZ)ej~c@O zJeNxJSKoPX9A3XH5)F6rgLz)%Ws`r$zdMvLE1p!HWrqH-Y*<`ar{CTJCi>U(?hGL$ z6zBGyHO{lU*dJ?iiNx=bmF{?0A{8|`iinjXGA{l`YGS^Ru;E6 zXyFvVeH##PgamS_+f;nooqhTZEZNWZM%eM3sUg73N+HC-X>n|MYktAN*jYf-g1YP7^;JD)`WoYvT=TcEI-+E6bhCm-bQkdy|$o{Ll7iW8dtIPfFTTizu$NtHj_UUH+ zZh$lDIx6q{EY4GSFM^t)K>+pTX#|g^lJa51<>K(YXZ&-`MP3SS76?x%NN;f#7u@gf zyZ$#nmQzXf18!q|W+lxfo|Sj^+5#61>R(Or*p$hQLBz?w9Favnxg1wG4{ zO*8enufZisQ8b5vN5kKqeP|pF?knty+R0s@+Goy$Yb#kKLL|1yakZ4uib+Aq5G7^G zJEr;hmPX3j>$+(Z+b}}GXGeHpGN<;JE|yn){vacn5XMcb7b`7^99ST`-u;E%D9NlVzrz`)lFhN-e**FF qup5A9`=DO(8R)<8|MfrFfF7r;LCDrajAQ(N$C;a08^6AEJ@!93PM@Lx diff --git a/docs/install.md b/docs/introduction/install.md similarity index 100% rename from docs/install.md rename to docs/introduction/install.md diff --git a/docs/introduction.md b/docs/introduction/introduction.md similarity index 100% rename from docs/introduction.md rename to docs/introduction/introduction.md diff --git a/docs/quick-start.md b/docs/introduction/quick-start.md similarity index 77% rename from docs/quick-start.md rename to docs/introduction/quick-start.md index 7c4663116..8e4908784 100644 --- a/docs/quick-start.md +++ b/docs/introduction/quick-start.md @@ -3,8 +3,7 @@ ## Overview This is a quick start guide. If you have a vague idea about how Tendermint -works and want to get started right away, continue. Otherwise, [review the -documentation](http://tendermint.readthedocs.io/en/master/). +works and want to get started right away, continue. ## Install @@ -135,10 +134,10 @@ This will install `go` and other dependencies, get the Tendermint source code, t Next, use the `tendermint testnet` command to create four directories of config files (found in `./mytestnet`) and copy each directory to the relevant machine in the cloud, so that each machine has `$HOME/mytestnet/node[0-3]` directory. Then from each machine, run: ``` -tendermint node --home ./mytestnet/node0 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656" -tendermint node --home ./mytestnet/node1 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656" -tendermint node --home ./mytestnet/node2 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656" -tendermint node --home ./mytestnet/node3 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656" +tendermint node --home ./mytestnet/node0 --proxy_app=kvstore --p2p.persistent_peers="ID1@IP1:26656,ID2@IP2:26656,ID3@IP3:26656,ID4@IP4:26656" +tendermint node --home ./mytestnet/node1 --proxy_app=kvstore --p2p.persistent_peers="ID1@IP1:26656,ID2@IP2:26656,ID3@IP3:26656,ID4@IP4:26656" +tendermint node --home ./mytestnet/node2 --proxy_app=kvstore --p2p.persistent_peers="ID1@IP1:26656,ID2@IP2:26656,ID3@IP3:26656,ID4@IP4:26656" +tendermint node --home ./mytestnet/node3 --proxy_app=kvstore --p2p.persistent_peers="ID1@IP1:26656,ID2@IP2:26656,ID3@IP3:26656,ID4@IP4:26656" ``` Note that after the third node is started, blocks will start to stream in diff --git a/docs/deploy-testnets.md b/docs/networks/deploy-testnets.md similarity index 100% rename from docs/deploy-testnets.md rename to docs/networks/deploy-testnets.md diff --git a/docs/terraform-and-ansible.md b/docs/networks/terraform-and-ansible.md similarity index 100% rename from docs/terraform-and-ansible.md rename to docs/networks/terraform-and-ansible.md diff --git a/docs/determinism.md b/docs/research/determinism.md similarity index 100% rename from docs/determinism.md rename to docs/research/determinism.md diff --git a/docs/transactional-semantics.md b/docs/research/transactional-semantics.md similarity index 100% rename from docs/transactional-semantics.md rename to docs/research/transactional-semantics.md diff --git a/docs/specification/configuration.md b/docs/tendermint-core/configuration.md similarity index 100% rename from docs/specification/configuration.md rename to docs/tendermint-core/configuration.md diff --git a/docs/how-to-read-logs.md b/docs/tendermint-core/how-to-read-logs.md similarity index 100% rename from docs/how-to-read-logs.md rename to docs/tendermint-core/how-to-read-logs.md diff --git a/docs/metrics.md b/docs/tendermint-core/metrics.md similarity index 100% rename from docs/metrics.md rename to docs/tendermint-core/metrics.md diff --git a/docs/specification/rpc.md b/docs/tendermint-core/rpc.md similarity index 100% rename from docs/specification/rpc.md rename to docs/tendermint-core/rpc.md diff --git a/docs/running-in-production.md b/docs/tendermint-core/running-in-production.md similarity index 100% rename from docs/running-in-production.md rename to docs/tendermint-core/running-in-production.md diff --git a/docs/using-tendermint.md b/docs/tendermint-core/using-tendermint.md similarity index 100% rename from docs/using-tendermint.md rename to docs/tendermint-core/using-tendermint.md diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md new file mode 100644 index 000000000..1d84b31c1 --- /dev/null +++ b/docs/tools/benchmarking.md @@ -0,0 +1,49 @@ +# tm-bench + +Tendermint blockchain benchmarking tool: + +- https://github.com/tendermint/tools/tree/master/tm-bench + +For example, the following: + + tm-bench -T 10 -r 1000 localhost:46657 + +will output: + + Stats Avg Stdev Max + Block latency 6.18ms 3.19ms 14ms + Blocks/sec 0.828 0.378 1 + Txs/sec 963 493 1811 + +## Quick Start + +[Install Tendermint](https://github.com/tendermint/tendermint#install) + +then run: + + tendermint init + tendermint node --proxy_app=kvstore + + tm-bench localhost:46657 + +with the last command being in a seperate window. + +## Usage + + tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] + + Examples: + tm-bench localhost:46657 + Flags: + -T int + Exit after the specified amount of time in seconds (default 10) + -c int + Connections to keep open per endpoint (default 1) + -r int + Txs per second to send in a connection (default 1000) + -v Verbose output + +## Development + + make get_vendor_deps + make test diff --git a/docs/tools/docker.md b/docs/tools/docker.md new file mode 100644 index 000000000..c6d09f6b9 --- /dev/null +++ b/docs/tools/docker.md @@ -0,0 +1,67 @@ +# Docker + +## Supported tags and respective `Dockerfile` links + +- `0.17.1`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/208ac32fa266657bd6c304e84ec828aa252bb0b8/DOCKER/Dockerfile) +- `0.15.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile) +- `0.13.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/a28b3fff49dce2fb31f90abb2fc693834e0029c2/DOCKER/Dockerfile) +- `0.12.1` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/457c688346b565e90735431619ca3ca597ef9007/DOCKER/Dockerfile) +- `0.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile) +- `0.11.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/9177cc1f64ca88a4a0243c5d1773d10fba67e201/DOCKER/Dockerfile) +- `0.10.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile) +- `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile) +- `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile) +- `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile) +- `develop` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/master/DOCKER/Dockerfile.develop) + +`develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch. + +## Quick reference + +- **Where to get help:** + https://cosmos.network/community + +- **Where to file issues:** + https://github.com/tendermint/tendermint/issues + +- **Supported Docker versions:** + [the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis) + +## Tendermint + +Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines. + +For more background, see the [introduction](https://tendermint.readthedocs.io/en/master/introduction.html). + +To get started developing applications, see the [application developers guide](https://tendermint.readthedocs.io/en/master/getting-started.html). + +## How to use this image + +### Start one instance of the Tendermint core with the `kvstore` app + +A quick example of a built-in app and Tendermint core in one container. + +``` +docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init +docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=kvstore +``` + +## Local cluster + +To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/master/Makefile) and run: + +``` +make build-linux +make build-docker-localnode +make localnet-start +``` + +Note that this will build and use a different image than the ones provided here. + +## License + +- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/master/LICENSE). + +## Contributing + +Contributions are most welcome! See the [contributing file](https://github.com/tendermint/tendermint/blob/master/CONTRIBUTING.md) for more information. diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md new file mode 100644 index 000000000..02c4c9c4c --- /dev/null +++ b/docs/tools/monitoring.md @@ -0,0 +1,77 @@ +# tm-monitor + +Tendermint blockchain monitoring tool; watches over one or more nodes, +collecting and providing various statistics to the user: + +- https://github.com/tendermint/tools/tree/master/tm-monitor + +## Quick Start + +### Docker + +Assuming your application is running in another container with the name +`app`: + + docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init + docker run -it --rm -v "/tmp:/tendermint" -p "46657:46657" --name=tm --link=app tendermint/tendermint node --proxy_app=tcp://app:46658 + + docker run -it --rm -p "46670:46670" --link=tm tendermint/monitor tm:46657 + +If you don't have an application yet, but still want to try monitor out, +use `kvstore`: + + docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init + docker run -it --rm -v "/tmp:/tendermint" -p "46657:46657" --name=tm tendermint/tendermint node --proxy_app=kvstore + + docker run -it --rm -p "46670:46670" --link=tm tendermint/monitor tm:46657 + +### Using Binaries + +[Install Tendermint](https://github.com/tendermint/tendermint#install) + +then run: + + tendermint init + tendermint node --proxy_app=kvstore + + tm-monitor localhost:46657 + +with the last command being in a seperate window. + +## Usage + + tm-monitor [-v] [-no-ton] [-listen-addr="tcp://0.0.0.0:46670"] [endpoints] + + Examples: + # monitor single instance + tm-monitor localhost:46657 + + # monitor a few instances by providing comma-separated list of RPC endpoints + tm-monitor host1:46657,host2:46657 + Flags: + -listen-addr string + HTTP and Websocket server listen address (default "tcp://0.0.0.0:46670") + -no-ton + Do not show ton (table of nodes) + -v verbose logging + +### RPC UI + +Run `tm-monitor` and visit http://localhost:46670 You should see the +list of the available RPC endpoints: + + http://localhost:46670/status + http://localhost:46670/status/network + http://localhost:46670/monitor?endpoint=_ + http://localhost:46670/status/node?name=_ + http://localhost:46670/unmonitor?endpoint=_ + +The API is available as GET requests with URI encoded parameters, or as +JSONRPC POST requests. The JSONRPC methods are also exposed over +websocket. + +## Development + + make get_tools + make get_vendor_deps + make test From d336cfe5d34f473521ed8218272c2210f55ec4b6 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Thu, 5 Jul 2018 23:36:49 -0700 Subject: [PATCH 10/24] RPC HTTP does not require quotes around int64/int/uint64/uint types --- rpc/lib/server/handlers.go | 66 +++++++++++++++++++++++++++-------- rpc/lib/server/http_params.go | 1 + 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 1bfe52536..7076bb750 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -18,9 +18,9 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" - types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" + types "github.com/tendermint/tendermint/rpc/lib/types" ) // RegisterRPCFuncs adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions. @@ -294,7 +294,7 @@ func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]re continue } - v, err, ok := nonJSONToArg(cdc, argType, arg) + v, err, ok := nonJSONStringToArg(cdc, argType, arg) if err != nil { return nil, err } @@ -303,7 +303,7 @@ func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]re continue } - values[i], err = _jsonStringToArg(cdc, argType, arg) + values[i], err = jsonStringToArg(cdc, argType, arg) if err != nil { return nil, err } @@ -312,26 +312,64 @@ func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]re return values, nil } -func _jsonStringToArg(cdc *amino.Codec, ty reflect.Type, arg string) (reflect.Value, error) { - v := reflect.New(ty) - err := cdc.UnmarshalJSON([]byte(arg), v.Interface()) +func jsonStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error) { + rv := reflect.New(rt) + err := cdc.UnmarshalJSON([]byte(arg), rv.Interface()) if err != nil { - return v, err + return rv, err } - v = v.Elem() - return v, nil + rv = rv.Elem() + return rv, nil } -func nonJSONToArg(cdc *amino.Codec, ty reflect.Type, arg string) (reflect.Value, error, bool) { +func nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error, bool) { + if rt.Kind() == reflect.Ptr { + rv_, err, ok := nonJSONStringToArg(cdc, rt.Elem(), arg) + if err != nil { + return reflect.Value{}, err, false + } else if ok { + rv := reflect.New(rt.Elem()) + rv.Elem().Set(rv_) + return rv, nil, true + } else { + return reflect.Value{}, nil, false + } + } else { + return _nonJSONStringToArg(cdc, rt, arg) + } +} + +// NOTE: rt.Kind() isn't a pointer. +func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error, bool) { + isIntString := RE_INT.Match([]byte(arg)) isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`) isHexString := strings.HasPrefix(strings.ToLower(arg), "0x") - expectingString := ty.Kind() == reflect.String - expectingByteSlice := ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8 + + var expectingString, expectingByteSlice, expectingInt bool + switch rt.Kind() { + case reflect.Int, reflect.Uint, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64: + expectingInt = true + case reflect.String: + expectingString = true + case reflect.Slice: + expectingByteSlice = rt.Elem().Kind() == reflect.Uint8 + } + + if isIntString && expectingInt { + qarg := `"` + arg + `"` + // jsonStringToArg + rv, err := jsonStringToArg(cdc, rt, qarg) + if err != nil { + return rv, err, false + } else { + return rv, nil, true + } + } if isHexString { if !expectingString && !expectingByteSlice { err := errors.Errorf("Got a hex string arg, but expected '%s'", - ty.Kind().String()) + rt.Kind().String()) return reflect.ValueOf(nil), err, false } @@ -340,7 +378,7 @@ func nonJSONToArg(cdc *amino.Codec, ty reflect.Type, arg string) (reflect.Value, if err != nil { return reflect.ValueOf(nil), err, false } - if ty.Kind() == reflect.String { + if rt.Kind() == reflect.String { return reflect.ValueOf(string(value)), nil, true } return reflect.ValueOf([]byte(value)), nil, true diff --git a/rpc/lib/server/http_params.go b/rpc/lib/server/http_params.go index 565060678..3c948c0ba 100644 --- a/rpc/lib/server/http_params.go +++ b/rpc/lib/server/http_params.go @@ -15,6 +15,7 @@ var ( dotAtom = atom + `(?:\.` + atom + `)*` domain = `[A-Z0-9.-]+\.[A-Z]{2,4}` + RE_INT = regexp.MustCompile(`^-?[0-9]+$`) RE_HEX = regexp.MustCompile(`^(?i)[a-f0-9]+$`) RE_EMAIL = regexp.MustCompile(`^(?i)(` + dotAtom + `)@(` + dotAtom + `)$`) RE_ADDRESS = regexp.MustCompile(`^(?i)[a-z0-9]{25,34}$`) From bd47ed989fc66fad19a745f4d0bf1e2863a1d56e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 6 Jul 2018 07:55:15 -0400 Subject: [PATCH 11/24] rpc/lib/server: add test for int parsing --- rpc/lib/server/parse_test.go | 49 ++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go index f71316906..7b0aacdbe 100644 --- a/rpc/lib/server/parse_test.go +++ b/rpc/lib/server/parse_test.go @@ -2,6 +2,8 @@ package rpcserver import ( "encoding/json" + "fmt" + "net/http" "strconv" "testing" @@ -134,7 +136,7 @@ func TestParseJSONArray(t *testing.T) { } } -func TestParseRPC(t *testing.T) { +func TestParseJSONRPC(t *testing.T) { assert := assert.New(t) demo := func(height int, name string) {} @@ -172,5 +174,48 @@ func TestParseRPC(t *testing.T) { } } - +} + +func TestParseURI(t *testing.T) { + + demo := func(height int, name string) {} + call := NewRPCFunc(demo, "height,name") + cdc := amino.NewCodec() + + cases := []struct { + raw []string + height int64 + name string + fail bool + }{ + // can parse numbers unquoted and strings quoted + {[]string{"7", `"flew"`}, 7, "flew", false}, + {[]string{"22", `"john"`}, 22, "john", false}, + {[]string{"-10", `"bob"`}, -10, "bob", false}, + // can parse numbers quoted, too + {[]string{`"7"`, `"flew"`}, 7, "flew", false}, + {[]string{`"-10"`, `"bob"`}, -10, "bob", false}, + // cant parse strings uquoted + {[]string{`"-10"`, `bob`}, -10, "bob", true}, + } + for idx, tc := range cases { + i := strconv.Itoa(idx) + // data := []byte(tc.raw) + url := fmt.Sprintf( + "test.com/method?height=%v&name=%v", + tc.raw[0], tc.raw[1]) + req, err := http.NewRequest("GET", url, nil) + assert.NoError(t, err) + vals, err := httpParamsToArgs(call, cdc, req) + if tc.fail { + assert.NotNil(t, err, i) + } else { + assert.Nil(t, err, "%s: %+v", i, err) + if assert.Equal(t, 2, len(vals), i) { + assert.Equal(t, tc.height, vals[0].Int(), i) + assert.Equal(t, tc.name, vals[1].String(), i) + } + } + + } } From 379e5a15cc79f87b42f06ae6dc6e8b48ac7600e4 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 6 Jul 2018 07:56:00 -0400 Subject: [PATCH 12/24] Uint64 --- rpc/lib/server/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 7076bb750..3ec5f81e3 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -347,7 +347,7 @@ func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect var expectingString, expectingByteSlice, expectingInt bool switch rt.Kind() { - case reflect.Int, reflect.Uint, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64: + case reflect.Int, reflect.Uint, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64: expectingInt = true case reflect.String: expectingString = true From 539722c02d1bce76abd9064dae0dee8c6fde37de Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Fri, 6 Jul 2018 13:31:39 -0400 Subject: [PATCH 13/24] ports --- docs/tools/benchmarking.md | 6 +++--- docs/tools/monitoring.md | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md index 1d84b31c1..17e32b5c5 100644 --- a/docs/tools/benchmarking.md +++ b/docs/tools/benchmarking.md @@ -6,7 +6,7 @@ Tendermint blockchain benchmarking tool: For example, the following: - tm-bench -T 10 -r 1000 localhost:46657 + tm-bench -T 10 -r 1000 localhost:26657 will output: @@ -24,7 +24,7 @@ then run: tendermint init tendermint node --proxy_app=kvstore - tm-bench localhost:46657 + tm-bench localhost:26657 with the last command being in a seperate window. @@ -33,7 +33,7 @@ with the last command being in a seperate window. tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] Examples: - tm-bench localhost:46657 + tm-bench localhost:26657 Flags: -T int Exit after the specified amount of time in seconds (default 10) diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md index 02c4c9c4c..4c49775e3 100644 --- a/docs/tools/monitoring.md +++ b/docs/tools/monitoring.md @@ -13,17 +13,17 @@ Assuming your application is running in another container with the name `app`: docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init - docker run -it --rm -v "/tmp:/tendermint" -p "46657:46657" --name=tm --link=app tendermint/tendermint node --proxy_app=tcp://app:46658 + docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm --link=app tendermint/tendermint node --proxy_app=tcp://app:26658 - docker run -it --rm -p "46670:46670" --link=tm tendermint/monitor tm:46657 + docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 If you don't have an application yet, but still want to try monitor out, use `kvstore`: docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init - docker run -it --rm -v "/tmp:/tendermint" -p "46657:46657" --name=tm tendermint/tendermint node --proxy_app=kvstore + docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm tendermint/tendermint node --proxy_app=kvstore - docker run -it --rm -p "46670:46670" --link=tm tendermint/monitor tm:46657 + docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 ### Using Binaries @@ -34,37 +34,37 @@ then run: tendermint init tendermint node --proxy_app=kvstore - tm-monitor localhost:46657 + tm-monitor localhost:26657 with the last command being in a seperate window. ## Usage - tm-monitor [-v] [-no-ton] [-listen-addr="tcp://0.0.0.0:46670"] [endpoints] + tm-monitor [-v] [-no-ton] [-listen-addr="tcp://0.0.0.0:26670"] [endpoints] Examples: # monitor single instance - tm-monitor localhost:46657 + tm-monitor localhost:26657 # monitor a few instances by providing comma-separated list of RPC endpoints - tm-monitor host1:46657,host2:46657 + tm-monitor host1:26657,host2:26657 Flags: -listen-addr string - HTTP and Websocket server listen address (default "tcp://0.0.0.0:46670") + HTTP and Websocket server listen address (default "tcp://0.0.0.0:26670") -no-ton Do not show ton (table of nodes) -v verbose logging ### RPC UI -Run `tm-monitor` and visit http://localhost:46670 You should see the +Run `tm-monitor` and visit http://localhost:26670 You should see the list of the available RPC endpoints: - http://localhost:46670/status - http://localhost:46670/status/network - http://localhost:46670/monitor?endpoint=_ - http://localhost:46670/status/node?name=_ - http://localhost:46670/unmonitor?endpoint=_ + http://localhost:26670/status + http://localhost:26670/status/network + http://localhost:26670/monitor?endpoint=_ + http://localhost:26670/status/node?name=_ + http://localhost:26670/unmonitor?endpoint=_ The API is available as GET requests with URI encoded parameters, or as JSONRPC POST requests. The JSONRPC methods are also exposed over From bef04114ef270a37a694973688fab2817fc29e8f Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Fri, 6 Jul 2018 14:25:04 -0400 Subject: [PATCH 14/24] docs: md fixes & latest tm-bench/monitor --- docs/tools/benchmarking.md | 75 +++++++++++++++++++++++++++----------- docs/tools/docker.md | 67 ---------------------------------- docs/tools/monitoring.md | 75 +++++++++++++++++++++++--------------- 3 files changed, 98 insertions(+), 119 deletions(-) delete mode 100644 docs/tools/docker.md diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md index 17e32b5c5..20c368e29 100644 --- a/docs/tools/benchmarking.md +++ b/docs/tools/benchmarking.md @@ -6,44 +6,75 @@ Tendermint blockchain benchmarking tool: For example, the following: - tm-bench -T 10 -r 1000 localhost:26657 +``` +tm-bench -T 10 -r 1000 localhost:26657 +``` will output: - Stats Avg Stdev Max - Block latency 6.18ms 3.19ms 14ms - Blocks/sec 0.828 0.378 1 - Txs/sec 963 493 1811 +``` +Stats Avg StdDev Max Total +Txs/sec 818 532 1549 9000 +Blocks/sec 0.818 0.386 1 9 +``` ## Quick Start -[Install Tendermint](https://github.com/tendermint/tendermint#install) +[Install Tendermint](../introduction/install) +This currently is setup to work on tendermint's develop branch. Please ensure +you are on that. (If not, update `tendermint` and `tmlibs` in gopkg.toml to use + the master branch.) then run: - tendermint init - tendermint node --proxy_app=kvstore +``` +tendermint init +tendermint node --proxy_app=kvstore +``` - tm-bench localhost:26657 +``` +tm-bench localhost:26657 +``` with the last command being in a seperate window. ## Usage - tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] +``` +tm-bench [-c 1] [-T 10] [-r 1000] [-s 250] [endpoints] - Examples: - tm-bench localhost:26657 - Flags: - -T int - Exit after the specified amount of time in seconds (default 10) - -c int - Connections to keep open per endpoint (default 1) - -r int - Txs per second to send in a connection (default 1000) - -v Verbose output +Examples: + tm-bench localhost:26657 +Flags: + -T int + Exit after the specified amount of time in seconds (default 10) + -c int + Connections to keep open per endpoint (default 1) + -r int + Txs per second to send in a connection (default 1000) + -s int + Size per tx in bytes + -v Verbose output +``` + +## How stats are collected + +These stats are derived by having each connection send transactions at the +specified rate (or as close as it can get) for the specified time. After the +specified time, it iterates over all of the blocks that were created in that +time. The average and stddev per second are computed based off of that, by +grouping the data by second. + +To send transactions at the specified rate in each connection, we loop +through the number of transactions. If its too slow, the loop stops at one second. +If its too fast, we wait until the one second mark ends. The transactions per +second stat is computed based off of what ends up in the block. + +Each of the connections is handled via two separate goroutines. ## Development - make get_vendor_deps - make test +``` +make get_vendor_deps +make test +``` diff --git a/docs/tools/docker.md b/docs/tools/docker.md deleted file mode 100644 index c6d09f6b9..000000000 --- a/docs/tools/docker.md +++ /dev/null @@ -1,67 +0,0 @@ -# Docker - -## Supported tags and respective `Dockerfile` links - -- `0.17.1`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/208ac32fa266657bd6c304e84ec828aa252bb0b8/DOCKER/Dockerfile) -- `0.15.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile) -- `0.13.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/a28b3fff49dce2fb31f90abb2fc693834e0029c2/DOCKER/Dockerfile) -- `0.12.1` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/457c688346b565e90735431619ca3ca597ef9007/DOCKER/Dockerfile) -- `0.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile) -- `0.11.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/9177cc1f64ca88a4a0243c5d1773d10fba67e201/DOCKER/Dockerfile) -- `0.10.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile) -- `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile) -- `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile) -- `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile) -- `develop` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/master/DOCKER/Dockerfile.develop) - -`develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch. - -## Quick reference - -- **Where to get help:** - https://cosmos.network/community - -- **Where to file issues:** - https://github.com/tendermint/tendermint/issues - -- **Supported Docker versions:** - [the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis) - -## Tendermint - -Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines. - -For more background, see the [introduction](https://tendermint.readthedocs.io/en/master/introduction.html). - -To get started developing applications, see the [application developers guide](https://tendermint.readthedocs.io/en/master/getting-started.html). - -## How to use this image - -### Start one instance of the Tendermint core with the `kvstore` app - -A quick example of a built-in app and Tendermint core in one container. - -``` -docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init -docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=kvstore -``` - -## Local cluster - -To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/master/Makefile) and run: - -``` -make build-linux -make build-docker-localnode -make localnet-start -``` - -Note that this will build and use a different image than the ones provided here. - -## License - -- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/master/LICENSE). - -## Contributing - -Contributions are most welcome! See the [contributing file](https://github.com/tendermint/tendermint/blob/master/CONTRIBUTING.md) for more information. diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md index 4c49775e3..5cc2ad3b1 100644 --- a/docs/tools/monitoring.md +++ b/docs/tools/monitoring.md @@ -12,18 +12,23 @@ collecting and providing various statistics to the user: Assuming your application is running in another container with the name `app`: - docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init - docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm --link=app tendermint/tendermint node --proxy_app=tcp://app:26658 +``` +docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init +docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm --link=app tendermint/tendermint node --proxy_app=tcp://app:26658 - docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 +docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 +``` If you don't have an application yet, but still want to try monitor out, use `kvstore`: - docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init - docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm tendermint/tendermint node --proxy_app=kvstore - - docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 +``` +docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init +docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm tendermint/tendermint node --proxy_app=kvstore +``` +``` +docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 +``` ### Using Binaries @@ -31,40 +36,48 @@ use `kvstore`: then run: - tendermint init - tendermint node --proxy_app=kvstore +``` +tendermint init +tendermint node --proxy_app=kvstore +``` - tm-monitor localhost:26657 +``` +tm-monitor localhost:26657 +``` with the last command being in a seperate window. ## Usage - tm-monitor [-v] [-no-ton] [-listen-addr="tcp://0.0.0.0:26670"] [endpoints] +``` +tm-monitor [-v] [-no-ton] [-listen-addr="tcp://0.0.0.0:26670"] [endpoints] - Examples: - # monitor single instance - tm-monitor localhost:26657 +Examples: + # monitor single instance + tm-monitor localhost:26657 - # monitor a few instances by providing comma-separated list of RPC endpoints - tm-monitor host1:26657,host2:26657 - Flags: - -listen-addr string - HTTP and Websocket server listen address (default "tcp://0.0.0.0:26670") - -no-ton - Do not show ton (table of nodes) - -v verbose logging + # monitor a few instances by providing comma-separated list of RPC endpoints + tm-monitor host1:26657,host2:26657 +Flags: + -listen-addr string + HTTP and Websocket server listen address (default "tcp://0.0.0.0:26670") + -no-ton + Do not show ton (table of nodes) + -v verbose logging +``` ### RPC UI Run `tm-monitor` and visit http://localhost:26670 You should see the list of the available RPC endpoints: - http://localhost:26670/status - http://localhost:26670/status/network - http://localhost:26670/monitor?endpoint=_ - http://localhost:26670/status/node?name=_ - http://localhost:26670/unmonitor?endpoint=_ +``` +http://localhost:26670/status +http://localhost:26670/status/network +http://localhost:26670/monitor?endpoint=_ +http://localhost:26670/status/node?name=_ +http://localhost:26670/unmonitor?endpoint=_ +``` The API is available as GET requests with URI encoded parameters, or as JSONRPC POST requests. The JSONRPC methods are also exposed over @@ -72,6 +85,8 @@ websocket. ## Development - make get_tools - make get_vendor_deps - make test +``` +make get_tools +make get_vendor_deps +make test +``` From 7b153bde317af83665402e95fcf74587e16a8e86 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 5 Jul 2018 11:39:42 +0400 Subject: [PATCH 15/24] add missing changelog entry Closes #1901 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e910e365..01910ed32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ BREAKING CHANGES: - [tmlibs] Update to v0.9.0 and merge into `libs` * remove `merkle` package (moved to `crypto/merkle`) - [rpc] `syncing` is now called `catching_up`. +- [config] Remove `max_block_size_txs` and `max_block_size_bytes` in favor of + consensus params from the genesis file. FEATURES - [cmd] Added metrics (served under `/metrics` using a Prometheus client; From ed016380765622b1aa411b9f3db9582ab10a8a04 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 5 Jul 2018 11:41:35 +0400 Subject: [PATCH 16/24] remove doc. about no longer existing config option --- docs/tendermint-core/running-in-production.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 1f87f9114..c6157fb3b 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -49,7 +49,7 @@ second TODO is to query the /status RPC endpoint. It provides the necessary info: whenever the node is syncing or not, what height it is on, etc. -``` +``` curl http(s)://{ip}:{rpcPort}/status ``` @@ -198,11 +198,6 @@ theres something to send its peers. You can also try lowering `timeout_commit` (time we sleep before proposing the next block). -- `consensus.max_block_size_txs` - -By default, the maximum number of transactions per a block is 10_000. -Feel free to change it to suit your needs. - - `p2p.addr_book_strict` By default, Tendermint checks whenever a peer's address is routable before From 9120fd5d14dc74e99da4cb3bebe2779fab8918ec Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 9 Jul 2018 13:01:23 +0400 Subject: [PATCH 17/24] unexport DecodeMessage functions Refs #646 --- blockchain/reactor.go | 19 +++++++------------ consensus/reactor.go | 12 +++++------- evidence/reactor.go | 12 +++++------- mempool/reactor.go | 8 +++----- p2p/pex/pex_reactor.go | 8 +++----- 5 files changed, 23 insertions(+), 36 deletions(-) diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 70a599bab..449a42ff0 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -5,12 +5,13 @@ import ( "reflect" "time" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" + + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" ) const ( @@ -174,7 +175,7 @@ func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage, // Receive implements Reactor by handling 4 types of messages (look below). func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { - msg, err := DecodeMessage(msgBytes) + msg, err := decodeMsg(msgBytes) if err != nil { bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) bcR.Switch.StopPeerForError(src, err) @@ -342,17 +343,11 @@ func RegisterBlockchainMessages(cdc *amino.Codec) { cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil) } -// DecodeMessage decodes BlockchainMessage. -// TODO: ensure that bz is completely read. -func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) { +func decodeMsg(bz []byte) (msg BlockchainMessage, err error) { if len(bz) > maxMsgSize { - return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", - len(bz), maxMsgSize) + return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } err = cdc.UnmarshalBinaryBare(bz, &msg) - if err != nil { - err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over") - } return } diff --git a/consensus/reactor.go b/consensus/reactor.go index 1cac32b80..76a15194b 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -9,11 +9,11 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" cstypes "github.com/tendermint/tendermint/consensus/types" + cmn "github.com/tendermint/tendermint/libs/common" tmevents "github.com/tendermint/tendermint/libs/events" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -184,7 +184,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) return } - msg, err := DecodeMessage(msgBytes) + msg, err := decodeMsg(msgBytes) if err != nil { conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) conR.Switch.StopPeerForError(src, err) @@ -1307,11 +1307,9 @@ func RegisterConsensusMessages(cdc *amino.Codec) { cdc.RegisterConcrete(&ProposalHeartbeatMessage{}, "tendermint/ProposalHeartbeat", nil) } -// DecodeMessage decodes the given bytes into a ConsensusMessage. -func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) { +func decodeMsg(bz []byte) (msg ConsensusMessage, err error) { if len(bz) > maxMsgSize { - return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", - len(bz), maxMsgSize) + return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } err = cdc.UnmarshalBinaryBare(bz, &msg) return diff --git a/evidence/reactor.go b/evidence/reactor.go index 7b22b8dba..bf11ac105 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -5,10 +5,10 @@ import ( "reflect" "time" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" + clist "github.com/tendermint/tendermint/libs/clist" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -73,7 +73,7 @@ func (evR *EvidenceReactor) RemovePeer(peer p2p.Peer, reason interface{}) { // Receive implements Reactor. // It adds any received evidence to the evpool. func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { - msg, err := DecodeMessage(msgBytes) + msg, err := decodeMsg(msgBytes) if err != nil { evR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) evR.Switch.StopPeerForError(src, err) @@ -204,11 +204,9 @@ func RegisterEvidenceMessages(cdc *amino.Codec) { "tendermint/evidence/EvidenceListMessage", nil) } -// DecodeMessage decodes a byte-array into a EvidenceMessage. -func DecodeMessage(bz []byte) (msg EvidenceMessage, err error) { +func decodeMsg(bz []byte) (msg EvidenceMessage, err error) { if len(bz) > maxMsgSize { - return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", - len(bz), maxMsgSize) + return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } err = cdc.UnmarshalBinaryBare(bz, &msg) return diff --git a/mempool/reactor.go b/mempool/reactor.go index e63ff58e8..96988be78 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -78,7 +78,7 @@ func (memR *MempoolReactor) RemovePeer(peer p2p.Peer, reason interface{}) { // Receive implements Reactor. // It adds any received transactions to the mempool. func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { - msg, err := DecodeMessage(msgBytes) + msg, err := decodeMsg(msgBytes) if err != nil { memR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) memR.Switch.StopPeerForError(src, err) @@ -174,11 +174,9 @@ func RegisterMempoolMessages(cdc *amino.Codec) { cdc.RegisterConcrete(&TxMessage{}, "tendermint/mempool/TxMessage", nil) } -// DecodeMessage decodes a byte-array into a MempoolMessage. -func DecodeMessage(bz []byte) (msg MempoolMessage, err error) { +func decodeMsg(bz []byte) (msg MempoolMessage, err error) { if len(bz) > maxMsgSize { - return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", - len(bz), maxMsgSize) + return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } err = cdc.UnmarshalBinaryBare(bz, &msg) return diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 2d93783df..0672abdbe 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -206,7 +206,7 @@ func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) { // Receive implements Reactor by handling incoming PEX messages. func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) { - msg, err := DecodeMessage(msgBytes) + msg, err := decodeMsg(msgBytes) if err != nil { r.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) r.Switch.StopPeerForError(src, err) @@ -670,11 +670,9 @@ func RegisterPexMessage(cdc *amino.Codec) { cdc.RegisterConcrete(&pexAddrsMessage{}, "tendermint/p2p/PexAddrsMessage", nil) } -// DecodeMessage implements interface registered above. -func DecodeMessage(bz []byte) (msg PexMessage, err error) { +func decodeMsg(bz []byte) (msg PexMessage, err error) { if len(bz) > maxMsgSize { - return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", - len(bz), maxMsgSize) + return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } err = cdc.UnmarshalBinary(bz, &msg) return From a19e857f2e3817444880898760bdf42dcce763ab Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 9 Jul 2018 13:11:41 +0400 Subject: [PATCH 18/24] [pex] switch to MustMarshalBinaryBare and UnmarshalBinaryBare Refs #646 --- p2p/pex/pex_reactor.go | 6 +++--- p2p/pex/pex_reactor_test.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 0672abdbe..e90665a37 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -287,7 +287,7 @@ func (r *PEXReactor) RequestAddrs(p Peer) { return } r.requestsSent.Set(id, struct{}{}) - p.Send(PexChannel, cdc.MustMarshalBinary(&pexRequestMessage{})) + p.Send(PexChannel, cdc.MustMarshalBinaryBare(&pexRequestMessage{})) } // ReceiveAddrs adds the given addrs to the addrbook if theres an open @@ -324,7 +324,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { // SendAddrs sends addrs to the peer. func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*p2p.NetAddress) { - p.Send(PexChannel, cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: netAddrs})) + p.Send(PexChannel, cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: netAddrs})) } // SetEnsurePeersPeriod sets period to ensure peers connected. @@ -674,7 +674,7 @@ func decodeMsg(bz []byte) (msg PexMessage, err error) { if len(bz) > maxMsgSize { return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize) } - err = cdc.UnmarshalBinary(bz, &msg) + err = cdc.UnmarshalBinaryBare(bz, &msg) return } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 6d6e91c38..629c9397a 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -134,11 +134,11 @@ func TestPEXReactorReceive(t *testing.T) { size := book.Size() addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} - msg := cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: addrs}) + msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) r.Receive(PexChannel, peer, msg) assert.Equal(t, size+1, book.Size()) - msg = cdc.MustMarshalBinary(&pexRequestMessage{}) + msg = cdc.MustMarshalBinaryBare(&pexRequestMessage{}) r.Receive(PexChannel, peer, msg) // should not panic. } @@ -154,7 +154,7 @@ func TestPEXReactorRequestMessageAbuse(t *testing.T) { assert.True(t, sw.Peers().Has(peer.ID())) id := string(peer.ID()) - msg := cdc.MustMarshalBinary(&pexRequestMessage{}) + msg := cdc.MustMarshalBinaryBare(&pexRequestMessage{}) // first time creates the entry r.Receive(PexChannel, peer, msg) @@ -191,7 +191,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { assert.True(t, sw.Peers().Has(peer.ID())) addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} - msg := cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: addrs}) + msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) // receive some addrs. should clear the request r.Receive(PexChannel, peer, msg) @@ -303,7 +303,7 @@ func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) { size := book.Size() addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} - msg := cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: addrs}) + msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) pexR.Receive(PexChannel, peer, msg) assert.Equal(t, size, book.Size()) From 0030a8e697b046f0afbc9a33958d00afda60fd77 Mon Sep 17 00:00:00 2001 From: Peng Zhong <172531+nylira@users.noreply.github.com> Date: Mon, 9 Jul 2018 17:19:45 +0800 Subject: [PATCH 19/24] Peng/deprecate aib data (#1926) * include ecosystem.json * update changelog * also include zarko's interview --- CHANGELOG.md | 3 +- docs/app-dev/ecosystem.json | 213 +++++++++++++++ docs/app-dev/ecosystem.md | 2 +- docs/interviews/tendermint-bft.md | 250 ++++++++++++++++++ docs/tendermint-core/running-in-production.md | 2 +- 5 files changed, 467 insertions(+), 3 deletions(-) create mode 100644 docs/app-dev/ecosystem.json create mode 100644 docs/interviews/tendermint-bft.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 01910ed32..3bd1b8ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## 0.23.0 -*TBD* +IMPROVEMENT +- [docs] Include `ecosystem.json` and `tendermint-bft.md` from deprecated `aib-data` repository. ## 0.22.0 diff --git a/docs/app-dev/ecosystem.json b/docs/app-dev/ecosystem.json new file mode 100644 index 000000000..363f18902 --- /dev/null +++ b/docs/app-dev/ecosystem.json @@ -0,0 +1,213 @@ +{ + "abciApps": [ + { + "name": "Cosmos SDK", + "url": "https://github.com/cosmos/cosmos-sdk", + "language": "Go", + "author": "Cosmos", + "description": + "A prototypical account based crypto currency state machine supporting plugins" + }, + { + "name": "cb-ledger", + "url": "https://github.com/block-finance/cpp-abci", + "language": "C++", + "author": "Block Finance", + "description": + "Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow" + }, + { + "name": "Clearchain", + "url": "https://github.com/tendermint/clearchain", + "language": "Go", + "author": "FXCLR", + "description": + "Application to manage a distributed ledger for money transfers that support multi-currency accounts" + }, + { + "name": "Ethermint", + "url": "https://github.com/tendermint/ethermint", + "language": "Go", + "author": "Tendermint", + "description": "The go-ethereum state machine run as an ABCI app" + }, + { + "name": "Merkle AVL Tree", + "url": "https://github.com/tendermint/merkleeyes", + "language": "Go", + "author": "Tendermint", + "description": "Tendermint IAVL tree implemented as an ABCI app" + }, + { + "name": "Burrow", + "url": "https://github.com/hyperledger/burrow", + "language": "Go", + "author": "Monax Industries", + "description": + "Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store" + }, + { + "name": "Merkle AVL Tree", + "url": "https://github.com/jTMSP/MerkleTree", + "language": "Java", + "author": "jTMSP", + "description": "Tendermint IAVL tree implemented as an ABCI app" + }, + { + "name": "TMChat", + "url": "https://github.com/wolfposd/TMChat", + "language": "Java", + "author": "jTMSP", + "description": "P2P chat using Tendermint" + }, + { + "name": "Comit", + "url": "https://github.com/zballs/comit", + "language": "Go", + "author": "Zach Balder", + "description": "Public service reporting and tracking" + }, + { + "name": "Passchain", + "url": "https://github.com/trusch/passchain", + "language": "Go", + "author": "trusch", + "description": + "Tool to securely store and share passwords, tokens and other short secrets" + }, + { + "name": "Passwerk", + "url": "https://github.com/rigelrozanski/passwerk", + "language": "Go", + "author": "Rigel Rozanski", + "description": "Encrypted storage web-utility backed by Tendermint" + }, + { + "name": "py-tendermint", + "url": "https://github.com/davebryson/py-tendermint", + "language": "Python", + "author": "Dave Bryson", + "description": + "A Python microframework for building blockchain applications with Tendermint" + }, + { + "name": "Stratumn SDK", + "url": "https://github.com/stratumn/sdk", + "language": "Go", + "author": "Stratumn", + "description": "SDK for Proof-of-Process networks" + }, + { + "name": "Lotion", + "url": "https://github.com/keppel/lotion", + "language": "Javascript", + "author": "Judd Keppel", + "description": + "A Javascript microframework for building blockchain applications with Tendermint" + }, + { + "name": "Tendermint Blockchain Chat App", + "url": "https://github.com/SaifRehman/tendermint-chat-app/", + "language": "Javascript", + "author": "Saif Rehman", + "description": + "This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3." + }, + { + "name": "BigchainDB", + "url": "https://github.com/bigchaindb/bigchaindb", + "language": "Python", + "author": "BigchainDB GmbH and the BigchainDB community", + "description": "Blockchain database" + }, + { + "name": "Mint", + "url": "https://github.com/Hashnode/mint", + "language": "Go", + "author": "Hashnode", + "description": "Build blockchain-powered social apps" + } + ], + "abciServers": [ + { + "name": "abci", + "url": "https://github.com/tendermint/abci", + "language": "Go", + "author": "Tendermint" + }, + { + "name": "js-abci", + "url": "https://github.com/tendermint/js-abci", + "language": "Javascript", + "author": "Tendermint" + }, + { + "name": "cpp-tmsp", + "url": "https://github.com/mdyring/cpp-tmsp", + "language": "C++", + "author": "Martin Dyring" + }, + { + "name": "jabci", + "url": "https://github.com/jTendermint/jabci", + "language": "Java", + "author": "jTendermint" + }, + { + "name": "ocaml-tmsp", + "url": "https://github.com/zballs/ocaml-tmsp", + "language": "Ocaml", + "author": "Zach Balder" + }, + { + "name": "abci_server", + "url": "https://github.com/KrzysiekJ/abci_server", + "language": "Erlang", + "author": "Krzysztof Jurewicz" + }, + { + "name": "py-abci", + "url": "https://github.com/davebryson/py-abci", + "language": "Python", + "author": "Dave Bryson" + }, + { + "name": "Spearmint", + "url": "https://github.com/dennismckinnon/spearmint", + "language": "Javascript", + "author": "Dennis McKinnon" + } + ], + "deploymentTools": [ + { + "name": "mintnet-kubernetes", + "url": "https://github.com/tendermint/tools", + "technology": "Docker and Kubernetes", + "author": "Tendermint", + "description": + "Deploy a Tendermint test network using Google's kubernetes" + }, + { + "name": "terraforce", + "url": "https://github.com/tendermint/tools", + "technology": "Terraform", + "author": "Tendermint", + "description": + "Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones" + }, + { + "name": "ansible-tendermint", + "url": "https://github.com/tendermint/tools", + "technology": "Ansible", + "author": "Tendermint", + "description": "Ansible playbooks + Tendermint" + }, + { + "name": "brooklyn-tendermint", + "url": "https://github.com/cloudsoft/brooklyn-tendermint", + "technology": "Clocker for Apache Brooklyn ", + "author": "Cloudsoft", + "description": "Deploy a tendermint test network in docker containers " + } + ] +} diff --git a/docs/app-dev/ecosystem.md b/docs/app-dev/ecosystem.md index 0f1951a08..7960e6c0d 100644 --- a/docs/app-dev/ecosystem.md +++ b/docs/app-dev/ecosystem.md @@ -7,7 +7,7 @@ Tendermint stack can be found at: We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to [this -file](https://github.com/tendermint/aib-data/blob/master/json/ecosystem.json) +file](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/ecosystem.json) to include your project. ## Other Tools diff --git a/docs/interviews/tendermint-bft.md b/docs/interviews/tendermint-bft.md new file mode 100644 index 000000000..8b3ad5743 --- /dev/null +++ b/docs/interviews/tendermint-bft.md @@ -0,0 +1,250 @@ +# Interview Transcript with Tendermint core researcher, Zarko Milosevic, by Chjango + +**ZM**: Regarding leader election, it's round robin, but a weighted one. You +take into account the amount of bonded tokens. Depending on how much weight +they have of voting power, they would be elected more frequently. So we do +rotate, but just the guys who are having more voting power would be elected +more frequently. We are having 4 validators, and 1 of them have 2 times more +voting power, they have 2 times more elected as a leader. + +**CC**: 2x more absolute voting power or probabilistic voting power? + +**ZM**: It's actually very deterministic. It's not probabilistic at all. See +[Tendermint proposal election specification][1]. In Tendermint, there is no +pseudorandom leader election. It's a deterministic protocol. So leader election +is a built-in function in the code, so you know exactly—depending on the voting +power in the validator set, you'd know who exactly would be the leader in round +x, x + 1, and so on. There is nothing random there; we are not trying to hide +who would be the leader. It's really well known. It's just that there is a +function, it's a mathematical function, and it's just basically—it's kind of an +implementation detail—it starts from the voting power, and when you are +elected, you get decreased some number, and in each round you keep increasing +depending on your voting power, so that you are elected after k rounds again. +But knowing the validator set and the voting power, it's very simple function, +you can calculate yourself to know exactly who would be next. For each round, +this function will return you the leader for that round. In every round, we do +this computation. It's all part of the same flow. It enforces the properties +which are: proportional to your voting power, you will be elected, and we keep +changing the leaders. So it can't happen to have one guy being more elected +than other guys, if they have the same voting power. So one time it will be guy +B, and next time it will be guy B1. So it's not random. + +**CC**: Assuming the validator set remains unchanged for a month, then if you +run this function, are you able to know exactly who is going to go for that +entire month? + +**ZM**: Yes. + +**CC**: What're the attack scenarios for this? + +**ZM**: This is something which is easily attacked by people who argue that +Tendermint is not decentralized enough. They say that by knowing the leader, +you can DDoS the leader. And by DDoSing the leader, you are able to stop the +progress. Because it's true. If you would be able to DDoS the leader, the +leader would not be able to propose and then effectively will not be making +progress. How we are addressing this thing is Sentry Architecture. So the +validator—or at least a proper validator—will never be available. You don't +know the ip address of the validator. You are never able to open the connection +to the validator. So validator is spawning sentry nodes and this is the single +administration domain and there is only connection from validator in the sense +of sentry nodes. And ip address of validator is not shared in the p2p network. +It’s completely private. This is our answer to DDoS attack. By playing clever +at this sentry node architecture and spawning additional sentry nodes in case, +for ex your sentry nodes are being DDoS’d, bc your sentry nodes are public, +then you will be able to connect to sentry nodes. this is where we will expect +the validator to be clever enough that so that in case they are DDoS’d at the +sentry level, they will spawn a different sentry node and then you communicate +through them. We are in a sense pushing the responsibility on the validator. + +**CC**: So if I understand this correctly, the public identity of the validator +doesn’t even matter because that entity can obfuscate where their real full +nodes reside via a proxy through this sentry architecture. + +**ZM**: Exactly. So you do know what is the address or identity of the validator +but you don’t know the network address of it; you’re not able to attack it +because you don’t know where they are. They are completely obfuscated by the +sentry nodes. There is now, if you really want to figure out….There is the +Tendermint protocol, the structure of the protocol is not fully decentralized +in the sense that the flow of information is going from the round proposer, or +the round coordinator, to other nodes, and then after they receive this it’s +basically like [inaudible: “O to 1”]. So by tracking where this information is +coming from, you might be able to identify who are the sentry nodes behind it. +So if you are doing some network analysis, you might be able to deduce +something. If the thing would be completely stuck, where the validator would +never change their sentry nodes or ip addresses of sentry nodes, it could be +possible to deduce something. This is where economic game comes into play. We +are doing an economics game there. We say that it’s a validator business. If +they are not able to hide themselves well enough, they’ll be DDoS’d and they +will be kicked out of the active validator set. So it’s in their interest. + +[Proposer Selection Procedure in Tendermint][1]. This is how it should work no +matter what implementation. + +**CC**: Going back to the proposer, lets say the validator does get DDoS’d, then +the proposer goes down. What happens? + +**ZM**: How the proposal mechanism works—there’s nothing special there—it goes +through a sequence of rounds. Normal execution of Tendermint is that for each +height, we are going through a sequence of rounds, starting from round 0, and +then we are incrementing through the rounds. The nodes are moving through the +rounds as part of normal procedure until they decide to commit. In case you +have one proposer—the proposer of a single round—being DDoS’d, we will probably +not decide in that round, because he will not be able to send his proposal. So +we will go to the next round, and hopefully the next proposer will be able to +communicate with the validators and then we’ll decide in the next round. + +**CC**: Are there timeouts between one round to another, if a round gets +skipped? + +**ZM**: There are timeouts. It’s a bit more complex. I think we have 5 timeouts. +We may be able to simplify this a bit. What is important to understand is: The +only condition which needs to be satisfied so we can go to the next round is +that your validator is able to communicate with more than 2/3rds of voting +power. To be able to move to the next round, you need to receive more than +2/3rd of voting power equivalent of pre-commit messages. + +We have two kinds of messages: 1) Proposal: Where the current round proposer is +suggesting how the next block should look like. This is first one. Every round +starts with proposer sending a proposal. And then there are two more rounds of +voting, where the validator is trying to agree whether they will commit the +proposal or not. And the first of such vote messages is called `pre-vote` and +the second one is `pre-commit`. Now, to be able to move between steps, between +a `pre-vote` and `pre-commit` step, you need to receive enough number of +messages where if message is sent by validator A, then also this message has a +weight, or voting power which is equal to the voting power of the validator who +sent this message. Before you receive more than 2/3 of voting power messages, you are not +able to move to the higher round. Only when you receive more than 2/3 of +messages, you actually start the timeout. The timeout is happening only after +you receive enough messages. And it happens because of the asynchrony of the +message communication so you give more time to guys with this timeout to +receive some messages which are maybe delayed. + +**CC**: In this way that you just described via the whole network gossiping +before we commit a block, that is what makes Tendermint BFT deterministic in a +partially synchronous setting vs Bitcoin which has synchrony assumptions +whereby blocks are first mined and then gossiped to the network. + +**ZM**: It's true that in Bitcoin, this is where the synchrony assumption comes +to play because if they're not able to communicate timely, they are not able to +converge to a single longest chain. Why are they not able to decrease timeout +in Bitcoin? Because if they would decrease, there would be so many forks that +they won't be able to converge to a single chain. By increasing this +complexity and the block time, they're able to have not so many forks. This is +effectively the timing assumption—the block duration in a sense because it's +enough time so that the decided block is propagated through the network before +someone else start deciding on the same block and creating forks. It's very +different from the consensus algorithms in a distributed computing setup where +Tendermint fits. In Tendermint, where we talk about the timing dependency, they +are really part of this 3-communication step protocol I just explained. We have +the following assumption: If the good guys are not able to communicate timely +and reliably without having message loss within a round, the Tendermint will +not make progress—it will not be making blocks. So if you are in a completely +asynchronous network where messages get lost or delayed unpredictably, +Tendermint will not make progress, it will not create forks, but it will not +decide, it will not tell you what is the next block. For termination, it's a +liveness property of consensus. It's a guarantee to decide. We do need timing +assumptions. Within a round, correct validators are able to communicate to each +other the consensus messages, not the transactions, but consensus messages. +They need to communicate in a timely and reliable fashion. But this doesn't +need to hold forever. It's just that what we are assuming when we say it's a +partially synchronous system, we assume that the system will be going through a +period of asynchrony, where we don't have this guarantee; the messages will be +delayed or some will be lost and then will not make progress for some period of +time, or we're not guaranteed to make progress. And the period of synchrony +where these guarantees hold. And if we think about internet, internet is best +described using such a model. Sometimes when we send a message to SF to +Belgrade, it takes 100 ms, sometimes it takes 300 ms, sometimes it takes 1 s. +But in most cases, it takes 100 ms or less than this. + +There is one thing which would be really nice if you understand it. In a global +wide area network, we can't make assumption on the communication unless we are +very conservative about this. If you want to be very fast, then we can't make +assumption and say we'll be for sure communicating with 1 ms communication +delay. Because of the complexity and various congestion issues on the network, +it might happen that during a short period of time, this doesn't hold. If this +doesn't hold and you depend on this for correctness of your protocol, you will +have a fork. So the partially synchronous protocol, most of them like +Tendermint, they don't depend on the timing assumption from the internet for +correctness. This is where we state: safety always. So we never make a fork no +matter how bad our estimates about the internet communication delays are. We'll +never make a fork, but we do make some assumptions, and these assumptions are +built-in our timeouts in our protocol which are actually adaptive. So we are +adapting to the current condition and this is where we're saying...We do assume +some properties, or some communication delays, to eventually hold on the +network. During this period, we guarantee that we will be deciding and +committing blocks. And we will be doing this very fast. We will be basically on +the speed of the current network. + +**CC**: We make liveness assumptions based on the integrity of the validator +businesses, assuming they're up and running fine. + +**ZM**: This is where we are saying, the protocol will be live if we have at +most 1/3, or a bit less than 1/3, of faulty validators. Which means that all +other guys should be online and available. This is also for liveness. This is +related to the condition that we are not able to make progress in rounds if we +don't receive enough messages. If half of our voting power, or half of our +validators are down, we don't have enough messages, so the protocol is +completely blocked. It doesn't make progress in a round, which means it's not +able to be signed. So it's completely critical for Tendermint that we make +progress in rounds. It's like breathing. Tendermint is breathing. If there is +no progress, it's dead; it's blocked, we're not able to breathe, that's why +we're not able to make progress. + +**CC**: How does Tendermint compare to other consensus algos? + +**ZM**: Tendermint is a very interesting protocol. From an academic point of +view, I'm convinced that there is value there. Hopefully, we prove it by +publishing it on some good conference. What is novel is, if we compare first +Tendermint to this existing BFT problem, it's a continuation of academic +research on BFT consensus. What is novel in Tendermint is that it somehow +merges consensus protocol with gossip. This is completely novel idea. +Originally, in BFT, people were assuming the single administration domain, +small number of nodes, local area network, 4-7 nodes max. If you look at the +research paper, 99% of them have this kind of setup. Wide area was studied but +there is significantly less work in wide area networks. No one studied how to +scale those protocols to hundreds or thousands of nodes before blockchain. It +was always a single administration domain. So in Tendermint now, you are able +to reach consensus among different administration domains which are potentially +hundreds of them in wide area network. The system model is potentially harder +because we have more nodes and wide area network. The second thing is that: +normally, in bft protocols, the protocol itself are normally designed in a way +that has two phases, or two parts. The one which is called normal case, which +is normally quite simple, in this normal case. In spite of some failures, which +are part of the normal execution of the protocol, like for example leader +crashes or leader being DDoS'd, they need to go through a quite complex +protocol, which is like being called view change or leader election or +whatever. These two parts of the same protocol are having quite different +complexity. And most of the people only understand this normal case. In +Tendermint, there is no this difference. We have only one protocol, there are +not two protocols. It's always the same steps and they are much closer to the +normal case than this complex view change protocol. + +_This is a bit too technical but this is on a high level things to remember, +that: The system it addresses it's harder than the others and the algorithm +complexity in Tendermint is simpler._ The initial goal of Jae and Bucky which +is inspired by Raft, is that it's simpler so normal engineers could understand. + +**CC**: Can you expand on the termination requirement? + +_Important point about Liveness in Tendermint_ + +**ZM**: In Tendermint, we are saying, for termination, we are making assumption +that the system is partially synchronous. And in a partially synchronous system +model, we are able to mathematically prove that the protocol will make +decisions; it will decide. + +**CC**: What is a persistent peer? + +**ZM**: It's a list of peer identities, which you will try to establish +connection to them, in case connection is broken, Tendermint will automatically +try to reestablish connection. These are important peers, you will really try +persistently to establish connection to them. For other peers, you just drop it +and try from your address book to connect to someone else. The address book is a +list of peers which you discover that they exist, because we are talking about a +very dynamic network—so the nodes are coming and going away—and the gossiping +protocol is discovering new nodes and gossiping them around. So every node will +keep the list of new nodes it discovers, and when you need to establish +connection to a peer, you'll look to address book and get some addresses from +there. There's categorization/ranking of nodes there. + +[1]: https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/consensus/proposer-selection.md diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index c6157fb3b..181d09428 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -16,7 +16,7 @@ logging level, you can do so by running tendermint with Validators are supposed to setup [Sentry Node Architecture](https://blog.cosmos.network/tendermint-explained-bringing-bft-based-pos-to-the-public-blockchain-domain-f22e274a0fdb) to prevent Denial-of-service attacks. You can read more about it -[here](https://github.com/tendermint/aib-data/blob/develop/medium/TendermintBFT.md). +[here](../interviews/tendermint-bft.md). ### P2P From 22133ef97aee5132c9c02b2bb81771d7746b6263 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 9 Jul 2018 13:54:22 +0400 Subject: [PATCH 20/24] add missing changelog entry Refs #1901 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bd1b8ac3..b6df76989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ BREAKING CHANGES: - [rpc] `syncing` is now called `catching_up`. - [config] Remove `max_block_size_txs` and `max_block_size_bytes` in favor of consensus params from the genesis file. +- [config] Change `max_packet_msg_size` back to `max_packet_msg_payload_size` FEATURES - [cmd] Added metrics (served under `/metrics` using a Prometheus client; From 4de9d42e4cb07b825fadb69abc983c1a5846d5e0 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Jul 2018 15:49:48 +0400 Subject: [PATCH 21/24] limit the number of requests in flights for Prometheus server (#1927) * limit the number of requests in flights for Prometheus server Closes #1804 Default to 1 because usually there's just one collector. * config: Up default for prom connections --- CHANGELOG.md | 4 ++++ Gopkg.lock | 6 +++--- Gopkg.toml | 2 +- config/config.go | 7 +++++++ config/toml.go | 6 ++++++ docs/tendermint-core/configuration.md | 6 ++++++ node/node.go | 10 ++++++++-- 7 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6df76989..76b91f962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ IMPROVEMENT - [docs] Include `ecosystem.json` and `tendermint-bft.md` from deprecated `aib-data` repository. +IMPROVEMENTS: +- [config] Add `instrumentation.max_open_connections`, which limits the number + of requests in flight to Prometheus server (if enabled). Default: 3. + ## 0.22.0 *July 2nd, 2018* diff --git a/Gopkg.lock b/Gopkg.lock index 17b74d74b..b1beaa208 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -180,13 +180,13 @@ version = "v1.0.0" [[projects]] + branch = "master" name = "github.com/prometheus/client_golang" packages = [ "prometheus", "prometheus/promhttp" ] - revision = "c5b7fccd204277076155f10851dad72b76a49317" - version = "v0.8.0" + revision = "d6a9817c4afc94d51115e4a30d449056a3fbf547" [[projects]] branch = "master" @@ -414,6 +414,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "71753a9d4ece4252d23941f116f5ff66c0d5da730a099e5a9867491d223ed93b" + inputs-digest = "6e854634d6c203278ce83bef7725cecbcf90023b0d0e440fb3374acedacbd5ad" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 28394b8b3..ecce0e417 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -88,7 +88,7 @@ [[constraint]] name = "github.com/prometheus/client_golang" - version = "0.8.0" + branch = "master" [[constraint]] branch = "master" diff --git a/config/config.go b/config/config.go index 22cecf989..2df8eb8e8 100644 --- a/config/config.go +++ b/config/config.go @@ -606,6 +606,12 @@ type InstrumentationConfig struct { // Address to listen for Prometheus collector(s) connections. PrometheusListenAddr string `mapstructure:"prometheus_listen_addr"` + + // Maximum number of simultaneous connections. + // If you want to accept more significant number than the default, make sure + // you increase your OS limits. + // 0 - unlimited. + MaxOpenConnections int `mapstructure:"max_open_connections"` } // DefaultInstrumentationConfig returns a default configuration for metrics @@ -614,6 +620,7 @@ func DefaultInstrumentationConfig() *InstrumentationConfig { return &InstrumentationConfig{ Prometheus: false, PrometheusListenAddr: ":26660", + MaxOpenConnections: 3, } } diff --git a/config/toml.go b/config/toml.go index 084325baa..858d9b31d 100644 --- a/config/toml.go +++ b/config/toml.go @@ -262,6 +262,12 @@ prometheus = {{ .Instrumentation.Prometheus }} # Address to listen for Prometheus collector(s) connections prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}" + +# Maximum number of simultaneous connections. +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = {{ .Instrumentation.MaxOpenConnections }} ` /****** these are for test settings ***********/ diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index e298dfd8f..0453bdad7 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -209,4 +209,10 @@ prometheus = false # Address to listen for Prometheus collector(s) connections prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 ``` diff --git a/node/node.go b/node/node.go index 0780891ef..9f6428ec1 100644 --- a/node/node.go +++ b/node/node.go @@ -8,6 +8,7 @@ import ( "net" "net/http" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" amino "github.com/tendermint/go-amino" @@ -599,8 +600,13 @@ func (n *Node) startRPC() ([]net.Listener, error) { // collectors on addr. func (n *Node) startPrometheusServer(addr string) *http.Server { srv := &http.Server{ - Addr: addr, - Handler: promhttp.Handler(), + Addr: addr, + Handler: promhttp.InstrumentMetricHandler( + prometheus.DefaultRegisterer, promhttp.HandlerFor( + prometheus.DefaultGatherer, + promhttp.HandlerOpts{MaxRequestsInFlight: n.config.Instrumentation.MaxOpenConnections}, + ), + ), } go func() { if err := srv.ListenAndServe(); err != http.ErrServerClosed { From af697d3c4abbf6b1e80cfb6010aef9556e8706bd Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Jul 2018 09:47:40 -0400 Subject: [PATCH 22/24] changelog, version --- CHANGELOG.md | 6 +++++- version/version.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bca6484d..9f2d41465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -IMPROVEMENTs +## 0.22.2 + +*July 10th, 2018* + +IMPROVEMENTS - [docs] Include `ecosystem.json` and `tendermint-bft.md` from deprecated `aib-data` repository. - [config] Add `instrumentation.max_open_connections`, which limits the number of requests in flight to Prometheus server (if enabled). Default: 3. diff --git a/version/version.go b/version/version.go index 68f1b4837..6ba0dcb42 100644 --- a/version/version.go +++ b/version/version.go @@ -4,13 +4,13 @@ package version const ( Maj = "0" Min = "22" - Fix = "1" + Fix = "2" ) var ( // Version is the current version of Tendermint // Must be a string because scripts like dist.sh read this file. - Version = "0.22.1" + Version = "0.22.2" // GitCommit is the current HEAD set using ldflags. GitCommit string From 223095d12fb3cd66b6a2deaee9dfdf808ee5405c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Jul 2018 09:48:01 -0400 Subject: [PATCH 23/24] remove Wait from consensus reactor OnStop --- consensus/reactor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/consensus/reactor.go b/consensus/reactor.go index 76a15194b..48ebcad23 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -80,7 +80,6 @@ func (conR *ConsensusReactor) OnStop() { conR.BaseReactor.OnStop() conR.unsubscribeFromBroadcastEvents() conR.conS.Stop() - conR.conS.Wait() } // SwitchToConsensus switches from fast_sync mode to consensus mode. From ac3b764b52868803fa16c37798228865477482b4 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Jul 2018 09:55:15 -0400 Subject: [PATCH 24/24] update changelog --- CHANGELOG.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2d41465..0f15507c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,18 @@ *July 10th, 2018* IMPROVEMENTS +- More cleanup post repo merge! - [docs] Include `ecosystem.json` and `tendermint-bft.md` from deprecated `aib-data` repository. - [config] Add `instrumentation.max_open_connections`, which limits the number of requests in flight to Prometheus server (if enabled). Default: 3. + +BUG FIXES +- [rpc] Allow unquoted integers in requests + - NOTE: this is only for URI requests. JSONRPC requests and all responses + will use quoted integers (the proto3 JSON standard). +- [consensus] Fix halt on shutdown + ## 0.22.1 *July 5th, 2018* @@ -29,7 +37,14 @@ BUG FIXES *July 2nd, 2018* BREAKING CHANGES: -- [config] Rename `skip_upnp` to `upnp`, and turn it off by default. +- [config] + * Remove `max_block_size_txs` and `max_block_size_bytes` in favor of + consensus params from the genesis file. + * Rename `skip_upnp` to `upnp`, and turn it off by default. + * Change `max_packet_msg_size` back to `max_packet_msg_payload_size` +- [rpc] + * All integers are encoded as strings (part of the update for Amino v0.10.1) + * `syncing` is now called `catching_up` - [types] Update Amino to v0.10.1 * Amino is now fully proto3 compatible for the basic types * JSON-encoded types now use the type name instead of the prefix bytes @@ -40,12 +55,6 @@ BREAKING CHANGES: * `tmlibs/merkle` -> `crypto/merkle`. Uses SHA256 instead of RIPEMD160 - [tmlibs] Update to v0.9.0 and merge into `libs` * remove `merkle` package (moved to `crypto/merkle`) -- [config] Remove `max_block_size_txs` and `max_block_size_bytes` in favor of - consensus params from the genesis file. -- [config] Change `max_packet_msg_size` back to `max_packet_msg_payload_size` -- [rpc] - * All integers are encoded as strings (part of the update for Amino v0.10.1) - * `syncing` is now called `catching_up` FEATURES - [cmd] Added metrics (served under `/metrics` using a Prometheus client;